|
Caio
|
 |
« on: July 03, 2009, 07:04:10 AM » |
|
This is for small questions not worth their own thread.
In Allegro, I have to draw a sprite to the screen that 1) comes from a sprite sheet, 2) is rotated and 3) is tinted. However, it seems that there are three separate functions, one for each of these points (masked_blit, rotate_sprite and draw_lit_sprite)! Do I have to copy the sprite to a temporary bitmap and edit it there, therefore having to blit it several times per frame instead of just one, or is there a better way to do it?
|
|
|
|
|
Logged
|
|
|
|
|
Zaphos
Guest
|
 |
« Reply #1 on: July 03, 2009, 01:40:25 PM » |
|
rotate_sprite (like all the *_sprite functions in general?) is masked, I think, so you don't need to do a masked blit. I'm not very familiar with the lit_sprite function but I think in that case you might need to draw_lit_sprite to a temp buffer and then rotate_sprite from the buffer to the screen ... at least, I don't see a better way offhand.
|
|
|
|
|
Logged
|
|
|
|
|
Caio
|
 |
« Reply #2 on: July 03, 2009, 03:32:16 PM » |
|
Still I need a way to choose which part of the sheet I want to blit...
|
|
|
|
|
Logged
|
|
|
|
|
dspencer
|
 |
« Reply #3 on: July 03, 2009, 05:10:09 PM » |
|
Hi! I'm using c++. I've got a grid which stores pointers to all the objects in the game, and allows me to quickly poll for collisions. When I do that, it gives me a list of Element*. However - what if I want to find out if I'm colliding with an Enemy_Element (a subclass of Element)? What would be the best way to do this? These seem to be my options:
1) Poll collisions takes a pointer to a predicate function as another argument - so I can make a function { return dynamic_cast<Enemy_Element*>e != NULL; } More likely, in this case, I would just make a function isEnemy() in element which returns false, then override it in the Enemy class to return true. 2) Make an enum, and associate each value with a different subtype. So, enum ELEMENT_TYPE { ELEMENT, ELEMENT_ENEMY }. each subclass would also have to have a isType() function which would recursively check if it was the right type. 3) Have the grid store each type of element in a different list and have a different function for each type. 4) Just thought of this - each type of Element has a list of qualities. Polling for collisions would also require what qualities they must fulfill. 5) ?? Any other options?
I guess any of those would work. My problem is that they feel clunky - it seems to me that there is probably some much easier way to do this. Except for 4... I think I might have just solved my own problem. But I'm still gunna post this, in case theres still some magic method that I'm not seeing.
Thanks!
|
|
|
|
|
Logged
|
|
|
|
Ivan
Owl Country
Level 10
alright, let's see what we can see
|
 |
« Reply #4 on: July 03, 2009, 06:24:42 PM » |
|
The lack of reflection in C++ is really a way to think about structuring your code with lack of reflection in mind. The proper object-oriented way to accomplish what you are trying to accomplish would be to move the responsibilities involving the particular subclass INTO the subclass, rather than accessing them externally. So say for example you want to play a specific sound when you are colliding with an enemy element. The proper way would be not to try to figure out if an element is an enemy type, but to have the enemy subclass override a virtual method that provides you the correct sound on collision. You can accomplish a lot with intermediate subclasses between your Element and your Enemy classes, like having a Collidable element subclass that the enemy then in turn subclasses, which provides features that you seems to be trying to apply externally.
I personally think that the lack of reflection features results in cleaner and faster code and forces you to think more about how your structure your classes. Sure there are times where you need to accomplish what you're trying to accomplish (i.e. your own reflection system), but these must never be in time-critical code and dynamic_cast should do the job just fine.
|
|
|
|
|
Logged
|
|
|
|
|
dspencer
|
 |
« Reply #5 on: July 03, 2009, 06:36:45 PM » |
|
Right - that's why I feel like I'm doing something wrong. But, I don't see how to do it that way...
So, to make things slightly more complicated, lets say I have a player object, enemy objects, and platform objects. They are all in my index, but their common ancestor is only Element - or, Collideable_Element, or whatnot. When a player collides with a platform rests on top, but when it collides with an Enemy it jumps back. Likewise, the enemy can collide with the platform, so I can't encapsulate all the logic in the enemy, because it doesn't know if its colliding with the player or a platform or another enemy.
Like I said, I think I have a non-perfect solution, which works fine, but I'm still interested in a better way to do this - even if just for the sake of knowing it.
|
|
|
|
« Last Edit: July 03, 2009, 06:39:52 PM by dspencer »
|
Logged
|
|
|
|
Ivan
Owl Country
Level 10
alright, let's see what we can see
|
 |
« Reply #6 on: July 03, 2009, 06:46:01 PM » |
|
So, to make things slightly more complicated, lets say I have a player object, enemy objects, and platform objects. They are all in my index, but their common ancestor is only Element - or, Collideable_Element, or whatnot. When a player collides with a platform rests on top, but when it collides with an Enemy it jumps back. Likewise, the enemy can collide with the platform, so I can't encapsulate all the logic in the enemy, because it doesn't know if its colliding with the player or a platform or another enemy.
Why not? You can have an onCollision(Element *collidedElement) method that each subclass implements. So for an enemy, it launches the colliding element back and for the platform, it just rests on top.
|
|
|
|
|
Logged
|
|
|
|
|
|
|
dspencer
|
 |
« Reply #8 on: July 03, 2009, 07:17:54 PM » |
|
Ooooh... I think I see. I'm not good at explaining these things, so I'll just pseudocode it. So (bear with me): class Element { virtual void onCollide(Element*) = 0; virtual void onCollide(Player*) = 0; virtual void onCollide(Enemy*) = 0; virtual void onCollide(Platform*) = 0; };
class Player { virtual void onCollide(Element* e) { e->onCollide(this); } virtual void onCollide(Player*) { /* only one player */ } virtual void onCollide(Enemy*) { /* jump back */ } virtual void onCollide(Platform*) { /*rest on platform*/ } }
class Enemy { virtual void onCollide(Element* e) { e->onCollide(this); } virtual void onCollide(Player*) { /* hit him */ } virtual void onCollide(Enemy*) { /* walk through */ } virtual void onCollide(Platform*) { /*rest on platform*/ } }
class Platform { virtual void onCollide(Element* e) { e->onCollide(this); } virtual void onCollide(Player*) { /* platforms do nothing! */ } virtual void onCollide(Enemy*) { } virtual void onCollide(Platform*) { } }
|
|
|
|
|
Logged
|
|
|
|
Ivan
Owl Country
Level 10
alright, let's see what we can see
|
 |
« Reply #9 on: July 03, 2009, 07:50:40 PM » |
|
The ultimate goal of inheritance is to not have your base classes be aware of their subclasses. What I had in mind is something like this: class Element { virtual void onCollide(Element*) = 0; };
class Enemy : public Element { void onCollide(Element* e) { e->applyYForce(); // or whatever } }
class Platform : public Element { void onCollide(Element* e) { e->restOnTop(); // or whatever } }
The problem with this approach though is that all elements will react the same way. But you can easily find a way around it by delegating the response to a virtual method within the other element. class Enemy : public Element { void onCollide(Element* e) { e->hurt(); } }
"hurt" in this case would damage a player or enemy and not do anything on an "Item" subclass of an "Element" or whatever. or say if you want it to only affect the player and not other enemies you can do something like put them into different teams and do: class Enemy : public Element { void onCollide(Element* e) { if(e->getTeamID() != this->teamID) e->hurt(); } }
|
|
|
|
« Last Edit: July 03, 2009, 07:57:05 PM by Ivan »
|
Logged
|
|
|
|
|
dspencer
|
 |
« Reply #10 on: July 03, 2009, 08:25:02 PM » |
|
First off, thanks for working this through with me, I really appreciate it!  My problem is then Enemies would hurt other enemies, which I don't want to do. Or, I can give elements all teams... but then platforms would have to be on a team. Which doesn't make sense. In fact, every type of element would have to have a method+data for everything any other element could do/would need to know. At least, things that could be done to it from the outside. Which I guess isn't a huge problem, but its frustrating.
|
|
|
|
|
Logged
|
|
|
|
Ivan
Owl Country
Level 10
alright, let's see what we can see
|
 |
« Reply #11 on: July 03, 2009, 08:48:34 PM » |
|
Well, I gave those as a quick example, so it was just a thought. The thing is that you goal is to model a certain group of interactions in your world in a certain group of abstracted base classes and you should try to conceptually separate these types of interactions and then create the appropriate base classes that deal with each type of interaction. This is actually kind of what you already thought of in your point #3, but you could have separate factories (though I use that term loosely) that create each class and then add them to the specific managers. So say your Enemy class inherits from Collidable and Monster (or Monster and Monster inherits from Collidable) and the factory that creates it can add it to two separate lists of Collidables and Monsters, so that the physics are modelled in the Collidables list and the player/enemy interaction is handled in the Monsters list (if player is also a subclass of Monster, etc.) This is just one way of thinking about it of course and in no way any kind of dogma.
|
|
|
|
|
Logged
|
|
|
|
|
BorisTheBrave
|
 |
« Reply #12 on: July 04, 2009, 12:53:08 AM » |
|
dspencer's approach is very legitimate. It is the visitor pattern. However, it quickly gets annoying when you have large amounts of classes, and it is hard to add extra classes. So it's rarely a good choice for games.
What you really want is multimethods, where you choose the function to use based on the two colliding classes. And potentially other details. But there aren't good implementations out there.
Try to keep the issue of deciding which function to use separate from the fact that inheritance hierarchies don't work particularly well at representing game objects.
Here's my two cents on the bog-simplest solution that actually addresses both issues: Every object has an arbitrary amount of categories, say strings. There could be a "Platform" category, "Enemy" etc. Special objects would probably get a category of their own. Then you keep a registry mapping category pairs to functions with priorities, and on collision call the highest priority matching function.
|
|
|
|
|
Logged
|
|
|
|
|
Stefan Schwartz
|
 |
« Reply #13 on: July 08, 2009, 10:16:41 PM » |
|
I don't know how quick this is, but I need a little help on an assignment for my class. Here's the actual assignment: http://www.santarosa.edu/~ssarkar/cs10sm09/cis10/a5.htmlIt's assignment 5.5, at the bottom of the page. Here's my code: #include <cstdlib> #include <iostream> #include <fstream> #include <string>
using namespace std;
int counter; ifstream inData; string fileName; char currchar; char prevchar;
int main() { cout << "Enter the input file name (quit to quit): "; cin >> fileName; inData.open(fileName.c_str()); while (fileName != "quit") { counter = -1; if ( !inData ) { cout << "Invalid input file" << endl; system("Pause"); return 1; } inData.get(prevchar); inData.get(currchar); while (inData) { if ((currchar == ' ' || currchar == '\n') && (prevchar != ' ' || prevchar != '\n')) { cout << prevchar << endl; cout << currchar << endl; counter++; }
prevchar = currchar; inData.get(currchar); } cout << "The number of words is " << counter << endl << endl; inData.close(); inData.clear(); cout << "Enter the name of the input file (quit to quit): "; cin >> fileName; inData.open(fileName.c_str()); } system("PAUSE"); return 0; } It's kind of a lot to ask for, but I'm getting a weird error with the program, where, instead of reading the correct number of words, its reading all the words and all the blank spaces too. My professor couldn't tell me what was wrong, so I was hoping one of you could help.
|
|
|
|
|
Logged
|
|
|
|
|
Aquin
|
 |
« Reply #14 on: July 08, 2009, 10:28:57 PM » |
|
Well, I mean...what if you have more than one whitespace character? I use this and it works. Keep in mind it's only slightly related, but you get the idea I hope. It counts the words. for(int i = 0; i < strlen(message); i++) { if(message[i] == '\n') { //we found a new word or a new line. } if(message[i] == ' ' && WordOn) WordOn = false; if(message[i] != ' ' && !WordOn) { //we found a word. } }
It's something like that.
|
|
|
|
|
Logged
|
I'd write a devlog about my current game, but I'm too busy making it.
|
|
|
|
Stefan Schwartz
|
 |
« Reply #15 on: July 08, 2009, 10:39:33 PM » |
|
I'll have to look at it again in the morning, but at my current knowledge level, I don't really understand that. What's message?
Sorry if this stuff seems really obvious, but I'm just beginning to program.
|
|
|
|
|
Logged
|
|
|
|
|
Aquin
|
 |
« Reply #16 on: July 08, 2009, 11:00:28 PM » |
|
Ahh, sorry dude. I'll dumb it down. I was in a hurry before...
let's see....
well, you should have access to strlen(message). message is the char* or string or whatever you're using. It gives you the length of the string.
So for(int i = 0; i < strlen(message); i++) { if(message) == ' ' || message == '\n')
//blah blah blah }
But honestly, I just use that hackneyed method for creating my own font. It sounds like you just want to count the number of words in a sentence.
In that case, I HIGHLY recommend using sscanf. Just look up the function on Google and you'll see why you should use it.
I'm rusty but it's probably like...
int words = 0; while(sscanf("%s", input) != '\0') //or something like this... I dunno. words++;
|
|
|
|
|
Logged
|
I'd write a devlog about my current game, but I'm too busy making it.
|
|
|
|
Zaphos
Guest
|
 |
« Reply #17 on: July 09, 2009, 12:14:14 AM » |
|
Here's my code: if ((currchar == ' ' || currchar == '\n') && (prevchar != ' ' || prevchar != '\n'))
(prevchar != ' ' || prevchar != '\n') is always true -- you probably meant && instead of || there. edit: Well, I mean...what if you have more than one whitespace character?
I think the prevchar stuff in his code was designed to handle that. In that case, I HIGHLY recommend using sscanf. Just look up the function on Google and you'll see why you should use it.
Then he'd need to read the whole file into a buffer though. Or use fscanf, but then he'd still have to worry about buffer size issues. It would probably be easier to use the C++ iostream functionality he's already familiar with to do the equivalent thing -- while (inData >> tempString) wordCount++; -- with tempString being a std::string.
|
|
|
|
« Last Edit: July 09, 2009, 12:35:30 AM by Zaphos »
|
Logged
|
|
|
|
|
Aquin
|
 |
« Reply #18 on: July 09, 2009, 12:32:38 AM » |
|
Well, he already has his input set up. I still the sscanf would work well here.... eh, either way really. And yeah, my code was a mistake. You really don't want to trust any code I write off-hand past midnight. 
|
|
|
|
|
Logged
|
I'd write a devlog about my current game, but I'm too busy making it.
|
|
|
|
Hideous
|
 |
« Reply #19 on: July 09, 2009, 01:09:51 AM » |
|
Still I need a way to choose which part of the sheet I want to blit...
There's the blit function for that.
|
|
|
|
|
Logged
|
|
|
|
|