Advertisement

click and drag sprite

Started by January 11, 2025 12:23 AM
56 comments, last by JoeJ 3 hours, 52 minutes ago

Looks good. Some changes needed for multiple sprites…

			vector <Sprite> sprites;
	sprites.push_back(Sprite(1900,725));
	sprites.push_back(Sprite(2005, 725));
	
			int dragSpriteIndex = -1; // 0 is a valid index to the first sprite, so we can't use it to indicate no sprite is selected. Thus we use -1 for that.
			
			//Sprite sprite; // not needed, using sprite[dragSpriteIndex] instead
			
			int prevX=0, prevY=0;
			bool quit = false;
			int deltaX = 0, deltaY = 0;
					else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT)
					{
						prevX = event.button.x;
						prevY = event.button.y;
						dragSpriteIndex = -1; // initially deselect in case no sprite is found
						
						
for (int i=0; i<(int)sprites.size(); i++) // loop over all sprites
						
{
							Sprite &sprite = sprites[i]; // make reference to the current sprite, so we don't need to type sprites[i] in follwing code...
							

							if (prevX >= sprite.x&&prevX <= sprite.x + 45 && prevY >= sprite.y&&prevY <= sprite.y + 45)
							{
								dragSpriteIndex = i; // select the current sprite
								break; // if a sprite is found, no need to check the others
							}
						}
					}
					else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT)
					{
						dragSpriteIndex = -1;
					}

You also nee to add such loop over all sprites for the drawing code at the end.

Then we need to move the selected sprite, and add a -1 check in case no sprite is selected:

					else if (event.type == SDL_MOUSEMOTION && dragSpriteIndex != -1)
					{
						Sprite &sprite = sprites[i];
						
						
// no changes needed after that...
						deltaX = event.motion.x - prevX;
						deltaY = event.motion.y - prevY;
						prevX = event.motion.x;
						prevY = event.motion.y;
						sprite.x += deltaX;
						sprite.y += deltaY;
					}

Then with some luck it should already work.

(Btw, instead using C for loop like my code you can also use std::iterators to iterate over std::containers, which can be faster due to using pointers instead index and base address. Personally i'm just too lazy to do so, as it's more typing.)

Well joe do I have to add anything to this code?

			vector <Sprite> sprites;
			sprites.push_back(Sprite(1900, 725));
			sprites.push_back(Sprite(2005, 725));
			int dragSpriteIndex = -1;
			Sprite sprite;
			int prevX=0, prevY=0;
			bool quit = false;
			int deltaX = 0, deltaY = 0;

			while (quit == false) {
				SDL_BlitSurface(gHelloWorld, NULL, gScreenSurface, NULL);

				SDL_Event event;
				while (SDL_PollEvent(&event)) {
					if (event.type == SDL_KEYDOWN)
					{
						Uint8 const* keys = SDL_GetKeyboardState(nullptr);
						if (keys[SDL_SCANCODE_ESCAPE] == 1)
							quit = true;

					}
					else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT)
					{						prevX = event.button.x;
						prevY = event.button.y;
						dragSpriteIndex = -1;
						for (int i = 0; i < (int)sprites.size(); i++)
						{
							Sprite &sprite = sprites[i];

							if (prevX >= sprite.x&&prevX <= sprite.x + 45 && prevY >= sprite.y&&prevY <= sprite.y + 45)
							{
								dragSpriteIndex = i;
								break;
							}
						}
					}
					else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT)
					{
						dragSpriteIndex = -1;
					}
					else if (event.type == SDL_MOUSEMOTION && dragSpriteIndex != -1)
					{
//						Sprite &sprite = sprites[i];
						deltaX = event.motion.x - prevX;
						deltaY = event.motion.y - prevY;
						prevX = event.motion.x;
						prevY = event.motion.y;
						sprite.x += deltaX;
						sprite.y += deltaY;
					}
				
					SDL_Rect rect;
					rect.x = 2005;
					rect.y = 725;
					SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect);

					SDL_Rect rect_one;
					rect_one.x = 2010;
					rect_one.y = 725;
					SDL_BlitSurface(gHelloWorld_two, NULL, gScreenSurface, &rect_one);

					SDL_Rect rect_two;
					rect_two.x = sprite.x;
					rect_two.y = sprite.y;
					SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect_two);
					SDL_UpdateWindowSurface((gWindow));
				
			}
Advertisement

pbivens67 said:
Well joe do I have to add anything to this code?

Hmmm… let's have a look…
I see it's just code. So my first proposal to add something is comments. You should comment your code to help yourself.
Personally i add comments in those cases for example:
If something is badly understood i add the related question to the comments. Your not a master at formulating questions, so maybe this would be good also to get better at that.
If some thing is missing, or meant to be improved, i add something like: // todo: handle errors
If something is complicated and not obvious, i add a little explanation in comments. Or at least i wish i would have done so.
And there are even standards which expect certain formatting rules on comments, so that tools like intellisense can display the documentation of a function to the coder just from hoovering a function call. Awesome. But sadly i'm a bit too lazy to do that too.

So as you see, comments are extremely useful!
But we need to force ourselves a bit to do the extra work.

Ok, moving on…
Ha! It's a comment which reveals a mistake:

pbivens67 said:
// Sprite &sprite = sprites[i];

It's my mistake, and you have copied it.
The varible i is not available here. I have meant the variable dragSpriteIndex, so this should be:

Sprite &sprite = sprites[dragSpriteIndex];

By using this variable we select the one currently selected sprite out of the many sprites in your vector.

However, i see you also have not yet updated the rendering code to render all the sprites.

So you're only alfway done. And the culprit is here:

			vector <Sprite> sprites;
			sprites.push_back(Sprite(1900, 725));
			sprites.push_back(Sprite(2005, 725));
			int dragSpriteIndex = -1;
//			Sprite sprite; // <- this is a single sprite. but we don't want that anymore. nevermore! we want many sprites!

Comment the sprite out and further compiler errors will show you the things you have to change next.
Basically all code sections which used the single sprite now either need to:
* Process one certain sprite from the vector, e.g. like when changing it's position from mouse input.
* Or process all sprites by iterating them in a loop, e.g. like when rendering the sprites

now it does not work at all

			vector <Sprite> sprites;
			sprites.push_back(Sprite(1900, 725));
			sprites.push_back(Sprite(2005, 725));
			int dragSpriteIndex = -1;
			Sprite sprite;
			int prevX=0, prevY=0;
			bool quit = false;
			int deltaX = 0, deltaY = 0;

			while (quit == false) {
				SDL_BlitSurface(gHelloWorld, NULL, gScreenSurface, NULL);

				SDL_Event event;
				while (SDL_PollEvent(&event)) {
					if (event.type == SDL_KEYDOWN)
					{
						Uint8 const* keys = SDL_GetKeyboardState(nullptr);
						if (keys[SDL_SCANCODE_ESCAPE] == 1)
							quit = true;

					}
					else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT)
					{						prevX = event.button.x;
						prevY = event.button.y;
						dragSpriteIndex = -1;
						for (int i = 0; i < (int)sprites.size(); i++)
						{
							Sprite &sprite = sprites[i];

							if (prevX >= sprite.x&&prevX <= sprite.x + 45 && prevY >= sprite.y&&prevY <= sprite.y + 45)
							{
								dragSpriteIndex = i;
								break;
							}
						}
					}
					else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT)
					{
						dragSpriteIndex = -1;
					}
					else if (event.type == SDL_MOUSEMOTION && dragSpriteIndex != -1)
					{
						Sprite &sprite = sprites[dragSpriteIndex];
						deltaX = event.motion.x - prevX;
						deltaY = event.motion.y - prevY;
						prevX = event.motion.x;
						prevY = event.motion.y;
						sprite.x += deltaX;
						sprite.y += deltaY;
					}
				
					SDL_Rect rect;
					rect.x = 2005;
					rect.y = 725;
					SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect);

					SDL_Rect rect_one;
					rect_one.x = 2010;
					rect_one.y = 725;
					SDL_BlitSurface(gHelloWorld_two, NULL, gScreenSurface, &rect_one);

					SDL_Rect rect_two;
					rect_two.x = sprite.x;
					rect_two.y = sprite.y;
					SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect_two);
					SDL_UpdateWindowSurface((gWindow));
				
			}

pbivens67 said:
now it does not work at all

Yes, becasue now it moves the correct sprite from the vector, but it still displays the wrong and single sprite.
To avoid this confusion, you need to comment ‘Sprite sprite;’ out.
Then you get multiple compiler errors. But it's helpful errors, revealing all the places where you still use the single sprite. You have to replace them all with the vector sprites, which will take time.

Maybe there is also some extra confusion because i have used the same name ‘sprite’ for referencing from the vector:
Sprite &sprite = sprites[i];
and
Sprite &sprite = sprites[dragSpriteIndex];

But that's irrelevant. You should not get compiler errors for those variables.

JoeJ said:
You have to replace them all with the vector sprites, which will take time.

what should I replace with what?

Advertisement

well joe what do you mean by replacing the sprites with vector sprites?

The variable Sprite sprite was your old storage.
std::vector<Sprite> sprites is your new storage you'll be replacing the old with.

This way you have one list of Sprites instead of a ton of individual Sprite instance variables.
Very good pattern for when you want to iterate over them all during test, update and/or draw

Dev careful. Pixel on board.
Buckle up. Everything will be revealed.

Am I on the right track with my updated code?

			vector <Sprite> sprites;
			sprites.push_back(Sprite(1900, 725));
			sprites.push_back(Sprite(2005, 725));
			int dragSpriteIndex = -1;
//			Sprite sprites;
			int prevX=0, prevY=0;
			bool quit = false;
			int deltaX = 0, deltaY = 0;

			while (quit == false) {
				SDL_BlitSurface(gHelloWorld, NULL, gScreenSurface, NULL);

				SDL_Event event;
				while (SDL_PollEvent(&event)) {
					if (event.type == SDL_KEYDOWN)
					{
						Uint8 const* keys = SDL_GetKeyboardState(nullptr);
						if (keys[SDL_SCANCODE_ESCAPE] == 1)
							quit = true;

					}
					else if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_LEFT)
					{						prevX = event.button.x;
						prevY = event.button.y;
						dragSpriteIndex = -1;
						for (int i = 0; i < (int)sprites.size(); i++)
						{
							Sprite &sprite = sprites[i];

							if (prevX >= sprites.x&&prevX <= sprites.x + 45 && prevY >= sprites.y&&prevY <= sprites.y + 45)
							{
								dragSpriteIndex = i;
								break;
							}
						}
					}
					else if (event.type == SDL_MOUSEBUTTONUP && event.button.button == SDL_BUTTON_LEFT)
					{
						dragSpriteIndex = -1;
					}
					else if (event.type == SDL_MOUSEMOTION && dragSpriteIndex != -1)
					{
						Sprite &sprite = sprites[dragSpriteIndex];
						deltaX = event.motion.x - prevX;
						deltaY = event.motion.y - prevY;
						prevX = event.motion.x;
						prevY = event.motion.y;
						sprites.x += deltaX;
						sprites.y += deltaY;
					}
				
					SDL_Rect rect;
					rect.x = 2005;
					rect.y = 725;
					SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect);

					SDL_Rect rect_one;
					rect_one.x = 2010;
					rect_one.y = 725;
					SDL_BlitSurface(gHelloWorld_two, NULL, gScreenSurface, &rect_one);

					SDL_Rect rect_two;
					rect_two.x = sprites.x;
					rect_two.y = sprites.y;
					SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect_two);
					SDL_UpdateWindowSurface((gWindow));
				
			}
 

There is still a naming error in your mouse down event test.
You're using the std::vector variable name without the index.
If you are going to make a reference, then use it. Drop the ‘s’ in your if statement.

if (prevX >= sprite.x && prevX <= sprite.x + 45 && prevY >= sprite.y && prevY <= sprite.y + 45)
{
	dragSpriteIndex = i;
	break;
}

but unnecessary and could be written as

if (prevX >= sprites[i].x && prevX <= sprites[i].x + 45 && prevY >= sprites[i].y && prevY <= sprites[i].y + 45)
{
	dragSpriteIndex = i;
	break;
}

There are more ways to iterate over std::vector containers.
Some more advanced than others.
The simplest shown next.

int i = 0;
for(auto item : sprites)
{
	if(prevX >= item.x && prevX <= item.x+45 && prevY >= item.y && prevY <= item.y+45)
	{
		dragSpriteIndex = i;
		break;
	}
	i++;
}

Dev careful. Pixel on board.
Buckle up. Everything will be revealed.

Advertisement