Enemies

Pages: 1234
closed account (N36fSL3A)
What is the best way to approach enemy handling in RPGs?

I was thinking Something like this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// I have a vector of enemies of different classes
std::vector<archer>   archers;
std::vector<riflemen> riflemen;
std::vector<pikemen>  pikemen;
// Like so^

// also a vector of allies
std::vector<Allies> allies;

void update()
{
    // Then I call each one, which takes a parameter of my unit class
    archer.update(player);
    archer.update(allies[0]);
    archer.update(allies[1]);
}

// etc.


But then i'd have to type that for EVERY single ally and whatever else interacts with enemies.

Any other way around this?

*mainly pointing at BHX because he's programming an RPG right now.
Last edited on by Fredbill30
closed account (o1vk4iN6)
I don't really see what you are trying to accomplish here. If two objects can interact with each other, why not just create one vector that holds a base class like "Entity" which every interactable object would then derive from.

Not sure where "archer" is defined or why it's being updated with the player.

But then i'd have to type that for EVERY single ally and whatever else interacts with enemies.


Yah if it's possible for every object to interact with each other than yah it is going to be expensive, that's why you use bounding volumes, binary space paritions, etc... to filter through and narrow down interactions that will bare fruit.
Last edited on
Yah if it's possible for every object to interact with each other than yah it is going to be expensive, that's why you use bounding volumes, binary space paritions, etc... to filter through and narrow down interactions that will bare fruit.

That's really the only relevant part, and I agree with it. I just want to point out that you shouldn't be manually calling update and should instead do it iteratively, but I'm sure you were just doing it that way for the sake of discussion.

It's fine, don't worry about it.
Ideally, Archer, Rifleman, and Pikeman would all extend a common NPC base class (or whatever name you think is best) and each one would remember whether it was allied with the player or an enemy with the player. This would involve polymorphism of course, and you would have to use a vector of (smart-)pointers.
Last edited on
closed account (N36fSL3A)
Yes, well my classes are currently :

1
2
3
4
Unit
    Enemy
        // Then the enemies would be here
    Player


I was planning on just doing the enemies deriving from a NPC class because I'd be easier.

NGen wrote:
I just want to point out that you shouldn't be manually calling update and should instead do it iteratively, but I'm sure you were just doing it that way for the sake of discussion.


Actually, I'm using a for loop for this. (Too much typing for EVERY one)
Last edited on by Fredbill30
@Fredbill: the player class and all NPC classes should extend a common base class with base behavior, basic interface, ect. - players and enemies should not have special treatment.
closed account (N36fSL3A)
I know. I meant I wanted to make a derived class for the NPC... you know what now that I think of it the NPC and the Enemy class should be one, and I should have a interger to determine the type.

But not right now. Right now I'm creating an Animation class.

EDIT: Wait, so you're telling me this will work?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Unit
{
    // Code
};
class NPC : Unit
{
    // Code
};

class Player : Unit
{
    // Code
};

vector<Unit> units;

int main()
{
    units.push_back(new Player);
    units.push_back(new NPC);    
    
    // I know I have to delete the memory... this is only
    // for example purposes
    
    return 0;
}
Last edited on by Fredbill30
No, because you're using new for a reference and you're not calling the constructor. But other than that, yes. Although I wouldn't put the player in the vector of objects because the player is a special object that can be controlled.
closed account (o1vk4iN6)
Player is not a special object, it is still in the confines of the game and in the limits of what a Unit should be able to do. If you will, instead of taking input from an AI controller it would take input from a player controller.

Also in case you didn't know, you'd want a virtual destructor in unit...

1
2
3
4
5
6
7
8
9
class Unit
{
public:
    virtual ~Unit() { }
    // Code
};

// ...
Last edited on
closed account (N36fSL3A)
Okay. Is it okay if I ask you guys a question with my Animation class?

EDIT:
chrisname wrote:
No, because you're using new for a reference

What do you mean?

And I am going to call the constructor. Will this work?
units.push_back(new Player());
Last edited on by Fredbill30
We would prefer if you asked us a question with your browser, your animation class should not be responsible for connecting to the internet and submitting post data.
closed account (N36fSL3A)
Lol.

Is it okay if I ask you guys a question with about my Animation class?
I was hinting at the fact that you probably don't need to ask "can I ask".
closed account (N36fSL3A)
Oh.

Well Here I go.

Whenever I try to hit the key wanted to change from animation handling in no class to a cleaned up version IN a class, it says something about bug assertion and the vector exceeded capacity.

Now, for whatever reason, it gives me an error whenever I try to read my stuff from the .ANIM file.

The error from reading file
http://prntscr.com/1838ph

Animation.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include "Animation.h"

int Animation::Init(std::string AnimFile, SDL_Surface* SpriteSheet)
{
	if(SpriteSheet == NULL)
	{
		std::cout << "SpriteSheet == NULL. Animation Failed to intialize\n";

		return 0;
	}
	Graphic = SpriteSheet;

	// Initialize variables
	X = 0;
	Y = 0;
	W = 0;
	H = 0;

	//nametemp = "";

	Frame = 0;
	Delay = 0;
	FrameChange = 0;

	// Make sure the animation isn't pause when it is
	// first displayed!
	Pause = false;

	// Now to load data from the animation file
	std::ifstream a_file;
	a_file.open(AnimFile); // Open the file

	// If the file didn't fail to open
	if(a_file != NULL)
	{
		// Coordinates for the clips
		int x = 0;
		int y = 0;
		char* n = "";
		AnimName = "";

		// Get the data from the file
		a_file >> n;    // Get the name of the animation
		a_file >> x;           // Get the x coordinate of where the clip starts
		a_file >> y;           // Get the y coordinate of where the clip starts
		a_file >> W;           // Get the width of a single frame
		a_file >> H;           // Get the height of a single frame
		a_file >> FrameChange; // Get the point at which the frame should change
		if(a_file.fail())
		{
			std::cout << "Failed to read file.";
			system("PAUSE");
		}

		AnimName = n;

		// Now load the clips from the graphic
		for(int c = 0; c < Graphic->w/W; c++)
		{
			SDL_Rect temp;
			
			temp.x = x;
			temp.y = y;
			temp.w = W;
			temp.h = H;

			// Add the temporary rectangle to the vector
			AnimClips.push_back(temp);

			// Now handle going to the next frame
			x += W;

			// If we reach the end of the graphic, we exit the loop
			if(x > Graphic->w) {c = Graphic->w/W;}
		}
	}
	else
	{
		std::cout << "Failure to load animation file\n.";
		a_file.close();
	}
	a_file.close();
}

void Animation::UpdateAnim()
{
	if(Pause == false)
	{
		// Every cycle add 1 to the delay
		Delay++;

		// If the delay hit the point where it needs to change the frame...
		if(Delay == FrameChange)
		{
			// Reset the delay
			Delay == 0;
			Frame++;    // Go to the next frame
			
			// If the frame is at the last, we reset the animation.
			if(Frame > Graphic->w/W - 1)
			{
				Frame = 0;
			}
		}
	}

	// If the animation is paused...
	else
	{
		// If the frame is paused, as soon as it's unpaused we don't want
		// it to immediately switch frames
		Delay = 0;
	}
}

void Animation::Render(SDL_Surface* Dest)
{
	Draw::DrawGFX(Graphic, AnimClips[Frame], Dest, X, Y);
}

void Animation::RenderObj(SDL_Surface* Dest)
{
	Draw::DrawGFX(Graphic, AnimClips[Frame], Dest, X - monitor.getCamera().x, Y - monitor.getCamera().y);
}


Animation.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include "Draw.h"
#include "Monitor.h"
#include <fstream>
#include <vector>

extern Monitor monitor;

#ifndef _ANIMATION_H_
#define _ANIMATION_H_

class Animation
{
	public:
		Animation()
		{

		}

		int Init(std::string AnimFile, SDL_Surface* SpriteSheet);

		void UpdateAnim(); // * Update the animation
		void Render(SDL_Surface* Dest);
		void RenderObj(SDL_Surface* Dest);

		void SetX(int x) {X = x;}
		void SetY(int y) {Y = y;}

		void NextFrame() {Frame++;} // * Go to the next frame
		void PrevFrame() {Frame--;} // * Go to the previous

		int  getFrame() {return Frame;}

		// * Toggle pausing
		void PauseAnim()
		{
			if(Pause)  {Pause = false;}
			if(!Pause) {Pause = true;}
		}

		~Animation()
		{
			SDL_FreeSurface(Graphic);
		}

	private:
		int X; // Coordinates
		int Y;

		// Width and height
		int W;
		int H;

		std::string AnimName; // - The name of the animation

		int Frame;       // - What frame the animation is on
		int Delay;       // - Once this is equal to FrameChange
		                 // the frame changes 
		int FrameChange; // - The CPU cycle the frame changes

		bool Pause;      // Represents if the animation is paused

		// Clips
		std::vector<SDL_Rect> AnimClips;

		SDL_Surface* Graphic;
};

#endif//_ANIMATION_H_ 


Player.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "Unit.h"

#ifndef _PLAYER_H_
#define _PLAYER_H_

class Player : public Unit
{
	public:
		Player() {MaxEnergy = 100; Energy = 100;}

		int Init(SDL_Surface* graphic, int x, int y, int vel, int health, int maxhealth);

		void SetCamera(Map &map);

		void Input(SDL_Event event);
		void Update(Map &map);
		void Render(SDL_Surface* Dest);

		void setName(char* name) {Name = name;}
		const char* getName() {const char* temp = Name.c_str(); return temp;}

		int getHealth()    {return Health;}
		int getMaxHealth() {return MaxHealth;}
		int getEnergy()    {return Energy;}
		int getMaxEnergy() {return MaxEnergy;}

		void Cleanup();

		SDL_Surface* getGraphic() {return Graphic;}

	private:
		std::string Name;

		SDL_Surface* showframe;

		int EnergyRegenT;
		int HealthRegenT;
};

#endif//_PLAYER_H_ 
Last edited on by Fredbill30
And I am going to call the constructor. Will this work?
units.push_back(new Player());


There is a magical thing called a compiler. Perhaps you should use one. So far, none of your code constructs will compile. If you used the magical compiler, you could figure that out for yourself.
closed account (N36fSL3A)
Ran out of room.

Player.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#include "Player.h"

int Player::Init(SDL_Surface* graphic, int x, int y, int vel, int health, int maxhealth)
{
	int _x = 0;
	int _y = 0;

	X = x;
	Y = y;
	Health = health;
	MaxHealth = maxhealth;

	Box.x = X - 5;
	Box.y = Y + 29;
	Box.w = 23;
	Box.h = 21;

	DownAnim  = Res.player_dwn;
	LeftAnim  = Res.player_lft;
	RightAnim = Res.player_rgt;
	UpAnim    = Res.player_up;

	// Start to load the clips from the images
	for(int c = 0; c < 4; c++)
	{
		DClips[c].x = _x;
		DClips[c].y = _y;

		DClips[c].w = PLYRG_W;
		DClips[c].h = PLYRG_H;

		_x += PLYRG_W;
	}

	_x = 0;
	_y = PLYRG_H;

	for(int c = 0; c < 4; c++)
	{
		LClips[c].x = _x;
		LClips[c].y = _y;

		LClips[c].w = PLYRG_W;
		LClips[c].h = PLYRG_H;

		_x += PLYRG_W;
	}
	_x = 0; 
	_y += PLYRG_H;

	for(int c = 0; c < 4; c++)
	{
		RClips[c].x = _x;
		RClips[c].y = _y;

		RClips[c].w = PLYRG_W;
		RClips[c].h = PLYRG_H;

		_x += PLYRG_W;
	}
	_x = 0;
	_y += PLYRG_H;

	for(int c = 0; c < 4; c++)
	{
		UClips[c].x = _x;
		UClips[c].y = _y;

		UClips[c].w = PLYRG_W;
		UClips[c].h = PLYRG_H;

		_x += PLYRG_W;
	}
	// End clip loading

	// Attempt to load the graphic
	if(graphic != NULL) {Graphic = graphic;}
	else                {std::cout << "Player graphic is equal to NULL\n"; return 1;}

	// Now set postions to avoid
	// initialization errors.
	frame = 0;       // - Set the frame to the first
	Position = DOWN; // - Set the player position to
	                 // face down
	delay = 0;       // - Set the frame delay to zero
	delaychange = 6; // - Set the amount of cycles
	                 // until the frame changes to 6
	moving = false;  // - Tell the program the player isn't moving
	action = false;  // - Tell the program the player isn't doin an action

	EnergyRegenT = 0;
	HealthRegenT = 0;
	return 0;
}

void Player::SetCamera(Map &map)
{
	// Set the camera position then check if it goes past map boundries
	monitor.setCameraPos(((X + Box.w/2) - monitor.getCamera().w / 2), ((Y + Box.h/2) - monitor.getCamera().w/2));

	// If the camera is in the end of the left side of the map or
	// the camera is in the end of the right side of the map prevent it from moving
	if(monitor.getCamera().x < 0) {monitor.setCameraPos(0, monitor.getCamera().y);}
	if(monitor.getCamera().x + monitor.getCamera().w > map.getMapW()) {monitor.setCameraPos(map.getMapW() - monitor.getCamera().w, monitor.getCamera().y);}
	
	// If the camera is at the top or bottom of the map prevent it from moving
	if(monitor.getCamera().y < 0) {monitor.setCameraPos(monitor.getCamera().x, 0);}
	if(monitor.getCamera().y + monitor.getCamera().h > map.getMapH()) {monitor.setCameraPos(monitor.getCamera().x, map.getMapH() - monitor.getCamera().h);}
}

void Player::Input(SDL_Event event)
{
	switch(event.type)
	{
		case SDL_KEYDOWN:
			switch(event.key.keysym.sym)
			{
				case SDLK_LEFT:
					Xvel -= 5;
					if(Health != 0){Position = LEFT;}
					moving = true;
				break;

				case SDLK_RIGHT:
					Xvel = 5;
					if(Health != 0){Position = RIGHT;}
					moving = true;
				break;

				case SDLK_UP:
					Yvel -= 5;
					if(Health != 0){Position = UP;}
					moving = true;
				break;

				case SDLK_DOWN:
					Yvel = 5;
					if(Health != 0){Position = DOWN;}
					moving = true;
				break;

				case SDLK_0:
					Health --;
				break;

				case SDLK_MINUS:
					Energy --;
				break;

				case SDLK_1:
					Health = MaxHealth;
					Energy = MaxEnergy;
				break;
				
				case SDLK_s:
					Health = 0;
					Energy = 0;
				break;
			}
		break;

		case SDL_KEYUP:
			switch(event.key.keysym.sym)
			{
				case SDLK_LEFT:
					if(Xvel < 0) {Xvel = 0;}
					frame = 0;
					if(Yvel == 0){moving = false;}
				break;

				case SDLK_RIGHT:
					if(Xvel > 0) {Xvel = 0;}
					frame = 0;
					if(Yvel == 0){moving = false;}
				break;

				case SDLK_UP:
					if(Yvel < 0) {Yvel = 0;}
					frame = 0;
					if(Xvel == 0){moving = false;}
				break;

				case SDLK_DOWN:
					if(Yvel > 0) {Yvel = 0;}
					frame = 0;
					if(Xvel == 0){moving = false;}
				break;
			}
		break;
	}
}

void Player::Update(Map &map)
{
	SDL_Color Black = {0, 0, 0, 0};
	std::string FRAME;
	if(Health < 0) {Health = 0;}
	if(Energy < 0) {Energy = 0;}

	DownAnim.UpdateAnim();
	RightAnim.UpdateAnim();
	LeftAnim.UpdateAnim();
	UpAnim.UpdateAnim();

	FRAME = Int2String::int2string(RightAnim.getFrame());

	showframe = TTF_RenderText_Solid(Res.stdFONT, FRAME.c_str(), Black);

	if(Health > 0)
	{
		// Handle Energy and Health regeneration
		EnergyRegenT++;
		if(EnergyRegenT > 175)
		{
			Energy += 1;
			EnergyRegenT = 0;
		}
		if(Energy > MaxEnergy) {Energy = MaxEnergy;}

		HealthRegenT++;
		if(HealthRegenT > 250)
		{
			Health += 1;
			HealthRegenT = 0;
		}
		if(Health > MaxHealth) {Health = MaxHealth;}
		// End of Energy and Health regeneration

		// If the player is moving, add one value to the delay
		if(moving == true)
		{
			delay++;
		}

		else
		{
			// If he/she isn't moving, make sure the player
			// doesn't get animated
			delay = 0;
		}

		// If the delay reaches the point where it
		// needs to add a frame, go to the next frame
		// and reset the delay
		if(delay == delaychange)
		{
			frame++;
			delay = 0;
		}

		// If the frame is larger than the frames in the
		// image, reset the frame to 0
		if(frame > 3)
		{
			frame = 0;
		}

		// Make copies of the X and Y values
		// incase we collide with something, so
		// we can revert the position
		int prevx = X;
		int prevy = Y;

		// Add the velocity to the
		// proper coordinates
		X += Xvel;
		Y += Yvel;

		// Update the collision box position
		Box.x = X + 5;
		Box.y = Y + 24;

		// If there is a X collision, revert the X coordinate
		if((Box.x < 0) || (Box.x + Box.w > map.getMapW()) || map.TouchSolid(Box) == true)
		{
			//std::cout << "Collision Detected.\n";
			X = prevx;
		}
		
		// If there is a Y collision, revert the Y coordinate
		if((Box.y < 0) || (Box.y + Box.h > map.getMapH()) || map.TouchSolid(Box) == true)
		{
			//std::cout << "Collision Detected.\n";

			Y = prevy;
		}

		SetCamera(map);
	}
}

void Player::Render(SDL_Surface* Dest)
{
	// If the player isn't dead
	if(Health != 0)
	{
		switch(Position)
		{
			case LEFT:
				//Draw::DrawGFX(Graphic, LClips[frame], Dest, X - monitor.getCamera().x, Y - monitor.getCamera().y);
				LeftAnim.RenderObj(Dest);
			break;

			case RIGHT:
				//Draw::DrawGFX(Graphic, RClips[frame], Dest, X - monitor.getCamera().x, Y - monitor.getCamera().y);
				RightAnim.RenderObj(Dest);
			break;

			case UP:
				Draw::DrawGFX(Graphic, UClips[frame], Dest, X - monitor.getCamera().x, Y - monitor.getCamera().y);
			break;

			case DOWN:
				Draw::DrawGFX(Graphic, DClips[frame], Dest, X - monitor.getCamera().x, Y - monitor.getCamera().y);
			break;
		}
	}

	// Display a red rectangle if the player is dead
	// (For debug purposes)
	else
	{
		SDL_Rect offset = {X - monitor.getCamera().x, Y - monitor.getCamera().y, 32, 48};

		SDL_FillRect(Dest, &offset, SDL_MapRGB(Dest->format, 255, 0, 0));
	}
	Draw::DrawGFX(showframe, Dest, 50, 32);
}

void Player::Cleanup()
{
	// Free the memory allocated when
	// the sprite sheet was loaded.
	SDL_FreeSurface(Graphic);
	SDL_FreeSurface(showframe);
}


Yea I know it's really messy, I'm working on cleaning it as this was just so I could see results.

Sorry for the wall of code. (Damn we really need spoilers.)
Last edited on by Fredbill30
closed account (o1vk4iN6)
Not sure what chris was talking about but both these call the default constructor, if there isn't one then they have different meaning.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct foo { int a; };

// memory leak just an example ...
std::cout << (new foo)->a << " " << (new foo())->a;

// output : ? 0
// "?" being whatever was in the memory 

struct bar { bar() : a(0) { } int a; };

std::cout << (new bar)->a << " " << (new bar())->a;

// output : 0 0
We have programming forums. Make use of them.

Although, "Please debug my hundreds of lines of code for me because I couldn't be bothered to reduce it to a minimal example which still illustrates the problem" doesn't usually garner many responses.
Well, I won't debug the whole thing, but I'll give you a hint- look at your updateanim() function. Is there something extra in there that changes the meaning of something?
closed account (N36fSL3A)
I've actually narrowed down the breaking at movement problem.

It's at eh, this line:
RightAnim.RenderObj(Dest);

Same thing happens at this line:
RightAnim.RenderObj(Dest);

I made a little bit of code to display the frame it's on. Turns out, the frame isn't being updated. (Shameful right?).

But I still can't figure out why it breaks here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
		// Coordinates for the clips
		int x = 0;
		int y = 0;
		char* n = "";
		AnimName = "";

		// Get the data from the file
		a_file >> n;    // Get the name of the animation
		a_file >> x;           // Get the x coordinate of where the clip starts
		a_file >> y;           // Get the y coordinate of where the clip starts
		a_file >> W;           // Get the width of a single frame
		a_file >> H;           // Get the height of a single frame
		a_file >> FrameChange; // Get the point at which the frame should change
		if(a_file.fail())
		{
			std::cout << "Failed to read file.";
			system("PAUSE");
		}

		AnimName = n;
}


EDIT: I think i narrowed the problem down more. I fixed the function above
Last edited on by Fredbill30
Pages: 1234