Jump to content

  • Log In with Google      Sign In   
  • Create Account


Trienco

Member Since 22 Aug 2001
Offline Last Active Yesterday, 10:03 PM

#5128331 rotating space ship

Posted by Trienco on 02 February 2014 - 10:50 PM

First, you have to translate your object so its center is at the origin. Then you rotate your object, and then you translate your object to its final destination.

 

That only works if you look at things from a "world view". Rotating (with glRotate) changes the local coordinate system of the object and the translation will move along the local coordinate system axes (just like rotation always happens around the current origin, aka. the objects position).

 

So the order in OpenGL (assuming the object is modeled around the origin) is glTranslate into position and then glRotate.

 

At least "thinking local" would seem a lot more intuitive than saying "OpenGL is doing everything backwards for some reason".

 

Of course those functions are technically deprecated and once handling matrices yourself it all boils down to multiplication order.




#5128102 Hexadecimals

Posted by Trienco on 01 February 2014 - 11:41 PM


And if you need the "0x" prefix you can do that in the string stream or insert it into the string after converting to upper case:

ss << "0x" << std::hex << myNumber; // either this
myHexString.insert( 0, "0x" ); // or this

 

Or simply:

ss << std::showbase << std::hex << myNumber;

 

Which has the distinct advantage that you don't have to manually add the 0x to every single hex value.




#5127127 Empty Array In Structures

Posted by Trienco on 28 January 2014 - 11:03 PM

Or the long overdue C++11 way:

 

char* buffer = Vec.data();

 

edit: okay, char to unsigned char means not getting rid of the cast. Which means being torn between the "proper" way of the ugly static_cast<char>() or the "evil" easy C-style cast (char*).




#5123777 Classes and use of 'New'

Posted by Trienco on 14 January 2014 - 11:09 PM

Minor nitpicks and remarks:

 

To fill the vector with default constructed objects, don't waste time writing a loop.

 

vector<Car> cars;

cars.resize(count);

 

Of course, if count is already known, you just want to do

vector<Car> cars(count);

 

 

Will do the same without any copying.

 

Rather than the unwieldy cars[cars.size()-1], just use cars.back().

 

To avoid copying, go the C++11 route:

 

Use

cars.emplace_back(100, 100);

 

rather than

cars.push_back(new Car(100, 100));




#5122991 Please check this for me. :)

Posted by Trienco on 12 January 2014 - 12:03 AM

One basic rule of thumb: "prefer non-friend non-member functions". Meaning: if there isn't a good reason for a function to be part of a class (like needing to access private members), it has no business being a member-function.

 

Unless one wants to argue that most of the C++ standard library is bad code, it's not just perfectly fine but even recommended not to stuff everything into objects "just because". Namespaces on the other hand are almost always a good idea for projects that are either complex and/or use multiple libraries.

 

Here are two examples of "C style functions":

 

//Explicitly declaring "no parameters" (in C: empty list = any number of parameters)

int function(void) { return 42; } 

 

//Skipping return and/or parameters type (C defaults to int, C++ won't compile)

function(x) { return 2*x; }




#5121315 Linear collision question

Posted by Trienco on 04 January 2014 - 11:50 PM

I follow that having two separate float values equaling the same is a low probability, but doesn't it at some point (despite how brief) have to equal a point on the line, say just before it crosses over the line. Does this mean that the update loop itself is too slow to catch the point as is?

 

Not only is it extremely unlikely that you will ever catch the exact moment of being exactly on the line, but more importantly: floats don't care about math.

 

sqrt(9.0f) * 3.0f == 4.5f * 2.0f... right? Wrong! Or at least not something you can rely on, because floats don't have infinite precision and you could easily end up comparing something like 8.999999999999999999 with 9.0000000000000001 (which are obviously NOT equal).

 

I also find it very important to be very aware of the big difference between testing for intersection and testing for collision. While intersection tests are often part of collision tests, they are also generally not sufficient, unless you make sure they are (like using fixed time steps and paying close attention to the minimum size of all objects). Only testing "does the ball intersect the wall in the new position" is useless if your ball is moving so fast that the new position is already behind the wall. You also generally don't just need to know _if_, but also _when/where_ the collision happened to correctly handle it.




#5110091 Inventory System for Noom Text Adventure Game

Posted by Trienco on 17 November 2013 - 11:19 PM

Sounds like most of all you're trying to avoid an insane mess of flags and nested if-blocks.

 

If your items are only strings and you expect to get a ton of them, you can use a std::set, but I doubt you'll come up with enough items to the point where a straight forward std::vector<string> isn't good enough.

 

Your events sound a lot like growing tables and I'd probably try to model them just as such. Things will be relatively flexible if you go look into std::bind (or boost::bind if you're stuck with the old standard).

 

It would all boil down to create conditions, a result and going through that table. For simplicity, let's assume all condition functions take a Player*.

 

bool hasDefeatedDragon(Player* p) { return p->dragonSlayerFlag; }
bool hasItem(Player* p, const string& item) { return find(p->inventory.begin(), p->inventory.end(), item) != p->inventory.end(); }
bool healthIsBelow(Player* p, int threshold) { return p->health < threshold; }
 
typedef std::function<bool, Player*> ConditionFunc;
 
struct EventTableEntry
{
   EventTableEntry(ConditionFunc f1, ConditionFunc f2, ConditionFunc f3, Event event);
 
  //You can use a list or template the number of preconditions if needed or use a dummy condition "ignore" that always returns true as filler
  ConditionFunc conditions[3];
  Event event;   
}
 
//Now why use std::bind? Because for this to work, all functions need to be called in the same way. Bind allows you to set certain parameters at construction time of the function object, so you won't have to pass it as a parameter when you actually call it.
 
EventTableEntry eventTable[] =
{
   EventTableEntry(isHealthy, std::bind(hasItem, _1, "sword"), ignore, spawnSkeleton);
   EventTableEntry(...);
};
 
Event* getAppropriateEvent()
{
   for(auto e : eventTable)
   {
        if (e.condition[0](player) && e.condition[1](player) && e.condition[2](player)) return &e.event;
   }
   return nullptr;
}

 

It's all crude pseudo code and taking some shortcuts, but might demonstrate the idea as such.




#5101179 "built-in loop" question

Posted by Trienco on 13 October 2013 - 10:32 PM

The place to start is to learn and gain actual experience and knowledge as a programmer, which (as already mentioned) will usually take a few years before you move from "trial & error" coding to really understanding how and why it works. You can argue "you don't need to understand how an engine works to drive a car", but you're not aiming at driving (as a user, who doesn't care how a program works), you're aiming at building. Using pre-made parts doesn't free you from the requirement of understanding how they work together.

 

There is nothing wrong with making "pretend" tutorials, where you try to clearly explain how things work. As long as you DO try to explain it and research it until you have a good understanding of it as well (rather than just parroting what you read online). Yet, those tutorials should probably never leave your local hard drive, because I can guarantee you that one or two years from now, you will look at them and feel embarrassed about the kind of nonsense you made up to explain stuff (after all, you gave the best example yourself... that other guy is incomplete and superficial at best and flat out wrong and misleading... not even sure if "at worst" or "on average" is more appropriate).




#5100706 How do I use multithreading?

Posted by Trienco on 11 October 2013 - 10:13 PM

 

Don't detach, so you can call join right before you absolutely need the thread to be finished (which will automatically block until the thread is done, without requiring any silly contraptions of flags and condition variables and inter-thread communication non-sense).

I think he wants the main thread to still be running the interactive game loop for as long as is needed for this background thread to complete, and only once it's complete does the main thread make use of the results.

 

That would of course be a cleaner solution, but in the end, it requires an actual plan what to do if you ever run into this situation (and how much longer this processing might take). If we are talking about something that should almost never happen and the delay is maybe 2s or less, is it worth the extra complexity to... well, what would you do? Show a loading screen or some kind of "don't worry, I'm still alive"-swirly icon thing?

 

If the main thread should continue, even when the data is needed and not ready, I'd be lazy and just go with std::async so I can query if the returned std::future is done yet (or wait for it with a timeout). While a primitive flag should be thread-safe, as long as one thread is only checking and the other is only eventually setting it, actually waiting for it (for example with a short timeout) would require adding a condition variable, a mutex and the flag, just to basically reinvent what is already there.




#5100433 How do I use multithreading?

Posted by Trienco on 10 October 2013 - 10:00 PM

Ever since the days of boost::thread (and std::thread is almost exactly the same) has there be no reason to introduce crutches like static member functions (or any other kind of "forwarding" function). When passing a member function to std::thread, the first parameter is automatically considered the object instance.

 

Having the SIG parameter for the function is pointless and redundant (ALL non-static member functions are being passed an instance, even if the language is hiding it). As a result, the call would have had to be 

 

std::thread(&SOLARSYSTEMIMAGEGENERATOR::ImageGenThread,this,this)

 

In other words, do as Paradigm Shifter already said and remove the pointless SIG parameter, then your original call should work just fine.

 

You are also still detaching the thread, which makes me wonder why you even bother making it a member? Read up on what detach does. You will have zero control over your thread and the thread-object will not be linked to the actual thread anymore. Don't detach, so you can call join right before you absolutely need the thread to be finished (which will automatically block until the thread is done, without requiring any silly contraptions of flags and condition variables and inter-thread communication non-sense).

 

If you want it to be nicely encapsulated, you could use std::future as well, but since you have no return value and don't need to carefully query "are we there yet", it has no benefit over starting your thread and calling join by the time you finally need that data.




#5100428 Unit Testing ftw?

Posted by Trienco on 10 October 2013 - 09:44 PM

One aspect of writing tests in advance (basically either kind of test) is that you're forced to think about corner cases and special circumstances to test. More than once, the result was being aware of those special cases before writing the actual implementation, resulting in code that had far fewer bugs from the start and ended up much cleaner, because you didn't end up fiddling in bug fixes after the fact.

 

If you write tests, I'd suggest doing so before you finish planing your actual implementation. Plan your system tests in parallel with the requirements (sometimes during writing a test you might realize there are requirements nobody thought about yet). Unit tests should be done in parallel with planing the interface for the unit (as sometimes writing the test will make you realize that your interface is broken). Afterwards during implementation, you will most likely go back and add more tests as you write test-worthy functions or algorithm.




#5098863 How do I use multithreading?

Posted by Trienco on 04 October 2013 - 10:29 PM

			std::thread helper1(GenerateDistanceList,this);
			helper1.detach();

 

Uhm... so now that you just went ahead and detached your thread, how are you ever going to find out if the thread is actually finished before you just cross your fingers and try to use the new star system?

 

When it comes to multi-threading, you can't just go and randomly make assumptions like "I'm sure the generation will be done in time". I've seen weird situation where threads wouldn't get to run for several seconds. At the very least, you should make sure to call join() before trying to enter the new system. That means you can't just detach and hope for the best. Which means you need to start thinking about who should own the thread object, because a function local variable won't do.




#5097573 include guards for large projects

Posted by Trienco on 29 September 2013 - 02:40 AM

pragma once is actually something I seem to have forgotten to mention. Unless one has to worry about obscure or outdated compilers, I'd always prefer that over the ifndef/define mess. Not just for potential optimizations, but because it's also less error prone in a copy/paste-happy environment (forget to update the include guard and figure out where all the weird errors are coming from).




#5097562 space wars

Posted by Trienco on 29 September 2013 - 12:54 AM

 

So the order of transforms of opengl is actually resversed.

 

 

Rather than thinking backwards, I'd prefer to think in terms of object space, which means that everything in OpenGL is perfectly logical and forward. All transformations are based on your current local coordinate system. A rotation will obviously rotate your coordinate system. x might not align with the global x anymore, but it will always be your objects "right". It also means that all rotations will always happen around your local origin, which also causes a lot less headache than trying to rotate an object around itself from a "world perspective".

 

Unless dealing with "outside influence" on your object, thinking in local coordinates is almost always more convenient and easier to reason about. It also means that nothing is actually happening "backwards".




#5097392 include guards for large projects

Posted by Trienco on 28 September 2013 - 12:22 AM

Forward declarations shouldn't be your last straw if nothing else helps, they should be your default whenever your header only uses pointers or references to something. You should always minimize the includes in header files, not only because of the mess you currently seem to find yourself in, but also because a huge web of includes will result in every source file being ridiculously huge and bloated after the preprocessor is done with it and make compilation take forever.

 

Also, since "include" is nothing but "copy/paste the file content right here", your double and triple checking is redundant, messy and in a best case scenario saves a few microseconds for the file access on any subsequent inclusion attempt.

 

You're drowning in a dependency mess and are flailing to stay above water. The solution isn't to learn how to swim, but to get out of the water. Object and sprite should be a very straight forward one-way dependency, so the question to ask is: why is object.h directly or indirectly including sprite.h? No amount of inclusions guards can fix circular includes.






PARTNERS