Advertisement

click and drag sprite

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

All I want to do is click and drag a sprite

else if (event.type == SDL_MOUSEBUTTONDOWN||SDL_MOUSEMOTION) {
						if (event.button.button == SDL_BUTTON_LEFT) {
							int mouseX, mouseY;
							SDL_GetMouseState(&mouseX, &mouseY);
							cout << mouseX << " " << mouseY << endl;
							if (mouseX > 522 && mouseX < 567 && mouseY>394 && mouseY < 439)
							{
								SDL_Rect rect_two;
								rect_two.x = 567;
								rect_two.y = 394;
								SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect_two);
								SDL_UpdateWindowSurface((gWindow));
							}
						}

pbivens67 said:
All I want to do is click and drag a sprite

A good exercise regarding realtime applications.

It's clear that you need some persistent data that lives not just for a single frame, or within a single function call.
Examples are the sprites position and the state of the mouse button.
SDL already does the latter for you, but you need to cara about the sprite. The easiest way would be to use a global variable. (Global variables are bad practice in general. It works for the simple test case, but if the program gets more complex global variables usually become a nightmare.)

Your code looks like you can click within some area, and if you do so it draws a rectangle over that area. But you can not yet move it, i guess.

I can give some example, but i don't know SDL, so you'd need to do some things differently, at the right callbacks SDL provides.

struct Sprite
{
	SDL_Rect rect;
	SDL_Color color; // idk if sdl has such type for color
};

Sprite globalSprite; // bad practice, but i know you love global variables and magic numbers, so...
Sprite *dragSprite = NULL; // if the user is dragging some sprite, this pointer will point to it. Otherwise pointer is zero.

void main()
{
	// set some initial state at application startup...
	
	globalSprite.rect.left = 522;
	//globalSprite.rect.right = ....
	globalSprite.color = white;
	
	// setup SDL, create Window, and all that...
	
	while (true) // some loop, not using callbacks for input, which SDL probably does
	{
		if (SDL_MouseButtonPressed()) // eventually grab the sprite and make it red
		{
			if (dragSprite == NULL && MouseCoordsInsideRectangle(globalSprite))
			{
				dragSprite = &globalSprite;
				dragSprite->color = RED;
			}
		}
		if (SDL_MouseButtonReleased()) // releasing the sprite - make it white again and set pointer to zero
		{
			if (dragSprite) dragSprite->color = WHITE;
			dragSprite = NULL;
		}
		if (dragSprite && SDL_MouseMoved()) // move the dragged Sprite
		{
			dragSprite->rect.left += SDL_MouseDeltaX();
			dragSprite->rect.up += SDL_MouseDeltaY();
			// assuming rect also has width and height which stays the same. 
			// If it has right and bottom we need to change that too ofc.
		}
		
		SDL_RenderRect (globalSprite.rect, globalSprite.color);
		
		SDL_Wait(60 fps);
	}
}

You need to figure out how to do this with SDL, but the code should help to see what global state is needed, and where in the loop this state is affected by what.

By using a pointer (not just a bool for example), the example can be easily extended to handle… multiple sprites! \:D/

Advertisement

I have worked on my code but it still does not click and drag a sprite

			bool isDragging = false;
			int startX, startY;
			int objectX = 0, objectY = 0;

			bool quit = false;
			while (quit == false) {
				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)
						if (event.button.button == SDL_BUTTON_LEFT) {
							isDragging = true;
							startX = event.button.x;
							startY = event.button.y;
						}
						else if (event.type == SDL_MOUSEBUTTONUP)
							if (event.button.button == SDL_BUTTON_LEFT) {
								isDragging = false;
							}
							else if (event.type == SDL_MOUSEMOTION)
								if (isDragging) {
									int offsetX = event.motion.x - startX;
									int offsetY = event.motion.y - startY;
									objectX += offsetX;
									objectY += offsetY;
									startX = event.motion.x;
									startY = event.motion.y;
								}
				}
			}
							int mouseX, mouseY;
							SDL_GetMouseState(&mouseX, &mouseY);
							cout << mouseX << " " << mouseY << endl;
							if (mouseX > 522 && mouseX < 567 && mouseY>394 && mouseY < 439)
							{
								SDL_Rect rect_two;
								rect_two.x = 567;
								rect_two.y = 394;
								SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect_two);
								SDL_UpdateWindowSurface((gWindow));
							}
					}
				}

I have to fix tabs so formatting becomes better readable.

It seems the section to render the sprite is outside the scope of the loop,
so the object is not rendered until we quit.

Another problem is, i expect the delta keeps growing even if movement would be zero.
So it will propell the sprite offscreen within a few frames, which is a typical mistake.

Might work now:

			bool isDragging = false;
			int prevX, prevY; // instead using a start, i use the previous coordinates to calculate movement relative to that each frame
			int objectX = 0, objectY = 0;

			bool quit = false;
			while (quit == false) 
			{
				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) 
					{
							isDragging = true;
							startX = event.button.x;
							startY = event.button.y;
					}
					else if (event.type == SDL_MOUSEBUTTONUP &&
							event.button.button == SDL_BUTTON_LEFT) 
					{
							isDragging = false;
					}
					else if (event.type == SDL_MOUSEMOTION/* && isDragging*/) // calculate mouse delta every frame, no matter if dragging or not
					{
						int deltaX = event.motion.x - prevX;
						int deltaY = event.motion.y - prevY;
						prevX = event.motion.x;
						prevY = event.motion.y;
						
						if (isDragging) // eventually move object
						{
							objectX += deltaX;
							objectY += deltaY;
							
						}

									//int offsetX = event.motion.x - startX;
									//int offsetY = event.motion.y - startY;
									//objectX += offsetX;
									//objectY += offsetY;
									//startX = event.motion.x;
									//startY = event.motion.y;
					}
				}
			//} // this must be after the rendering code, so it is executed in the loop, not after it
			
							//int mouseX, mouseY;
							//SDL_GetMouseState(&mouseX, &mouseY);
							//cout << mouseX << " " << mouseY << endl;
							//if (mouseX > 522 && mouseX < 567 && mouseY>394 && mouseY < 439)
							
							cout << objectX << " " << objectX << endl; // should change only if mouse moves

				//{
								SDL_Rect rect_two;
								
								rect_two.x = objectX; // give the sprite the changing coordinates
								rect_two.y = objectY;
								rect_two.width = rect_two.height = 10; // you need to give it some size too
								
								SDL_BlitSurface(gHelloWorld_one, NULL, gScreenSurface, &rect_two);
								SDL_UpdateWindowSurface((gWindow));
				//}
			}
		
Advertisement