I'm still a somewhat beginner in SDL, and I've come accross a problem that I am little lost on. I've been loosely following LazyFoo's tutorials along with some other additional resources.
The problem is trying to create an animated sprite onto a window.
I managed to isolate the malfunctioning code to whenever I press a key down.
When I press a key down, a new window opens up in addition to the one previously intialized using the SDL_CreateWindow function.
case SDL_KEYDOWN:
int frame = 0;
//Clear Screen
SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(renderer);
//Render Current frame
SDL_Rect* currentFrame = &iKey.MegamanRun[frame / 6];
iKey.mmxrender((sx - currentFrame->w) / 2, (sy - currentFrame->h) / 2, currentFrame);
//update screen
SDL_RenderPresent(renderer);
//Next frame
++frame;
I'm using a simple megaman sprite, if that helps you understand some of the names.
Specifically, the
iKey.mmxrender((sx - currentFrame->w) / 2, (sy - currentFrame->h) / 2, currentFrame);
is causing new windows to pop up.
the mmxrender function is in a different class on a different file, sx and sy are the size of the window.
void Megaman::mmxrender(int x, int y, SDL_Rect* clip)
{
Init mkey;
//Set rendering space
SDL_Rect renderQuad = { x, y, iWidth, iHeight };
//Set clip dimensions
if (clip != NULL)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
//Render to screen
SDL_RenderCopy(mkey.renderer, iTexture, clip, &renderQuad);
}
Not sure how to proceed, any help is appreciated.
Related
This is the code as it is but I'm having trouble making SDL_Rect work or cairo move to / line to. It produces a blank black window. I found out that it is possible for cairo to draw on an SDL2 window but don't know how I would go about making it work. Majority of the code I see uses GTK+.
SDL_Window* mainWindow;
SDL_Renderer* mainRenderer;
SDL_CreateWindowAndRenderer(1280, 960, SDL_WINDOW_SHOWN, &mainWindow, &mainRenderer);
cairo_surface_t* surface;
cairo_t* cr;
surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1200, 900);
cr = cairo_create(surface);
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
cairo_set_line_width(cr, 25);
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
cairo_move_to(cr, 100.0, 100.0);
cairo_line_to(cr, 500, 500);
cairo_stroke(cr);
unsigned char* data;
data = cairo_image_surface_get_data(surface);
SDL_Texture* texture;
SDL_Rect rect = {0, 0, 100, 100};
texture = SDL_CreateTexture(mainRenderer, SDL_PIXELFORMAT_ABGR8888,
SDL_TEXTUREACCESS_STREAMING, 100, 200);
SDL_UpdateTexture(texture, &rect, data, 400);
// Main program loop
while (1)
{
SDL_Event event;
if (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
{
SDL_DestroyRenderer(mainRenderer);
SDL_DestroyWindow(mainWindow);
SDL_Quit();
break;
}
}
SDL_RenderClear(mainRenderer);
SDL_RenderCopy(mainRenderer, texture, NULL, NULL);
SDL_RenderPresent(mainRenderer);
}
// Cleanup and quit
cairo_destroy(cr);
cairo_surface_destroy(surface);
Your texture is 100x200 and you're only updating its (0,0)(100,100) rectangle from cairo image data, but with cairo you only started drawing at (100,100), so entire area is black. In addition, your pitch when updating texture is incorrect - it is byte length of source data line; your cairo image have width of 1200 and its format requires 4 bytes per pixel; neglecting padding it is 1200*4, not 400 (note - if format is different, e.g. 3 bytes per pixel, padding may be important - refer to cairo documentation to check if it pads its rows if you're going to use that format). So there are two solutions:
Use cairo to produce full image you want, e.g. don't use (100,100) offset with move_to, or copy entire image to SDL texture. Then only correcting pitch is enough.
Copy part of cairo data to texture,
e.g.
const unsigned int bpp = 4;
const unsigned int pitch = 1200*bpp;
SDL_UpdateTexture(texture, &rect,
data // offset pointer to start at 'first' pixel you want to copy
+ 100*pitch // skip first 100 rows
+ 100*bpp, // and first 100 pixels
pitch // pitch is the same - it refers to source image, not destination
);
I'm just setting up a simple renderer using LWJGL 3 and when it runs on my external display it looks like I expect but when resize it on my Macbook it shrinks the viewport. Then if I move the window it fixes it. Here's the code that I run when I init my GL stuff and on window resize.
public void resize(int width, int height)
{
val near = 0.1
val far = 100.0
GL11.glViewport(0, 0, width, height);
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
float aspect = width / (float)height
float fov = (float)(45.0 / 360.0 * Math.PI)
float fH = Math.tan(fov) * near
float fW = fH * aspect
GL11.glFrustum(-fW, fW, -fH, fH, near, far);
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
}
Here's what I'm seeing
Wrong
Right
If I'm running it on my external display it doesn't change, it's always right.
Reading the size of the frame buffer was the answer here. I create the window with the pixel size the user passes in and then read the frameBuffer size to pass to glViewport.
public Window(String title, int width, int height)
{
_errorCallback = GLFWErrorCallback.createPrint(System.err);
GLFW.glfwSetErrorCallback(_errorCallback);
if (GLFW.glfwInit() != GLFW.GLFW_TRUE)
{
throw IllegalStateException("Unable to initialize GLFW");
}
GLFW.glfwDefaultWindowHints();
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
GLFW.glfwWindowHint(GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE);
_window = GLFW.glfwCreateWindow(width, height, title ?: "", 0, 0);
if (_window == 0L)
{
throw RuntimeException("Failed to create window");
}
// Setup Callbacks
// Get the resolution of the primary monitor
GLFWVidMode vidmode = GLFW.glfwGetVideoMode(GLFW.glfwGetPrimaryMonitor());
// Center our window
GLFW.glfwSetWindowPos(_window, (vidmode.width() - width) / 2, (vidmode.height() - height) / 2);
// Make the OpenGL context current
GLFW.glfwMakeContextCurrent(_window);
// Enable v-sync
GLFW.glfwSwapInterval(1);
// Make the window visible
GLFW.glfwShowWindow(_window);
}
Then I read the frame buffer size to pass into glViewport and glFrustum.
public Vector2 frameBufferSize()
{
IntBuffer bufferWidth = BufferUtils.createIntBuffer(4);
IntBuffer bufferHeight = BufferUtils.createIntBuffer(4);
GLFW.glfwGetFramebufferSize(_window, bufferWidth, bufferHeight);
return Vector2(bufferWidth.get(0), bufferHeight.get(0));
}
I have a 16:9 display where I'd like to show fullscreen SDL window which is in 4:3 mode.SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) sets the window to the left side of the screen and leaves a big black bar to the right.
I'd like to center the window and have black bars on the left and the right side.
It seems that SDL_SetWindowPosition(window, x, y) has no effect on window when it is in fullscreen mode. Can I center the fullscreen window in SDL2?
There are two situation:
(1) display with renderer and texture base on window size.
(2) display with screen and surface base on pixel.
For (1) here is a simple solution base on setting view port for renderer.(no testing but a guideline)
void SDL_SetRendererViewportRatio_4_3(SDL_Window *window,
SDL_Renderer *renderer
SDL_Rect *viewport) {
Uint8 r, g, b, a;
SDL_GetRenderDrawColor(renderer, &r, &g, &b, &a);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_SetRenderDrawColor(renderer, r, g, b, a);
int w, h;
SDL_GetWindowSize(window, &w, &h);
if (w * 3 > h * 4) {
viewport->w = h * 4 / 3;
viewport->h = h;
} else {
viewport->w = w;
viewport->h = w * 3 / 4;
}
viewport->x = (w - viewport->w) / 2;
viewport->y = (h - viewport->h) / 2;
SDL_RenderSetViewport(renderer, viewport);
}
Note that you should call this function whenever window changed size.
For (2) I guess you should calculate the coordinate of surface and draw big black bars by yourself. It is more difficult that I cannot prove simple solution.
I am just trying to scale (make bigger proportionally) my character based on windows weight and height. How can I do that?
I tried SDL_BlitScaled(newsurface, NULL, screen, NULL); but it did not work.
My code:
SDL_Surface* screen = SDL_GetWindowSurface(win);
SDL_Surface* mySprite = IMG_Load( SPRITE_PNG_PATH);
SDL_Rect rcSprite;
rcSprite.x = 250;
rcSprite.y = 100;
SDL_BlitSurface(mySprite, NULL, screen, &rcSprite);
The best way to accomplish this is to have a middle surface, whose width and height are your game's native resolution. You then render the entire frame to this surface, and render that surface to the window using the SDL_BlitScaled function.
For example, if you want your native resolution to be 600x400, you would create a 600x400 surface, blit your character and whatever else to that surface, and then finally scaled blit that surface to the window. If you resize your window to 1200x800, everything will look twice as big.
SDL_Surface* screen = SDL_GetWindowSurface(win);
// Create a surface with our native resolution
int NATIVE_WIDTH = 600;
int NATIVE_HEIGHT = 400;
SDL_Surface* frame = SDL_CreateRGBSurface(0, NATIVE_WIDTH, NATIVE_HEIGHT,
screen->format->BitsPerPixel, screen->format->Rmask,
screen->format->Gmask, screen->format->Bmask, screen->format->Amask);
assert(frameSurface);
SDL_Surface* mySprite = IMG_Load( SPRITE_PNG_PATH);
SDL_Rect rcSprite;
rcSprite.x = 250;
rcSprite.y = 100;
// Blit everything to the intermediate surface, instead of the screen.
SDL_BlitSurface(mySprite, NULL, frame, &rcSprite);
// Once we've drawn the entire frame, we blit the intermediate surface to
// the window, using BlitScaled to ensure it gets scaled to fit the window.
SDL_BlitScaled(frame, NULL, screen, NULL);
I just started to make another small game in SDL, i made my 2 players - represented by the top half of a circle. So far, i draw a background, and the two players on top.
if (SDL_Init(SDL_INIT_VIDEO) < 0 ) return 1;
if (!(screen = SDL_SetVideoMode(WIDTH, HEIGHT, DEPTH,SDL_HWSURFACE| SDL_DOUBLEBUF)))
{
SDL_Quit();
return 1;
}
SDL_Surface* surface2 = SDL_CreateRGBSurface(SDL_HWSURFACE, WIDTH, HEIGHT, 32,
rmask, gmask, bmask, amask);
SDL_FillRect(surface2, 0, SDL_MapRGBA(surface2->format,0,0xAA,0,0xFF));
//in the while loop
SDL_BlitSurface(surface2,0,screen,0);
When i move the player, it moves 10px per movement. I have implemented smooth movement, with timers, so that is not the case. The intersting part is, that when i choose to leave the surface2 black - without a fillrect at the start, then everything works smooth.
I now fill the screen surface with a color, and it also runs smoothely. So what might be the problem?