Do I need to move the tiles or the player in a 2d tile world? - image

I'm currently creating a 2d tile game and I'm wondering if the tiles has to move or the character.
I ask this question because I already have created the "2d tile map" but it is running too slow, and I can't fix it. I tried everything now and the result is that I get 30 fps.
The reason that it is running too slow is because every 1ms with a timer, the tiles are being redrawn. But I can't figure out how to fix this problem.
This is how I make the map :
public void makeBoard()
{
for (int i = 0; i < tileArray.GetLength(0); i++)
{
for (int j = 0; j < tileArray.GetLength(1); j++)
{
tileArray[i, j] = new Tile() { xPos = j * 50, yPos = i * 50 };
}
}
}
Here I redraw each 1ms or higher the tiles and sprites :
private void Wereld_Paint_1(object sender, PaintEventArgs e)
{
//label1.Text = k++.ToString();
using (Graphics grap = Graphics.FromImage(bmp))
{
for (int i = 0; i < tileArray.GetLength(0); i++)
{
for (int j = 0; j < tileArray.GetLength(1); j++)
{
grap.DrawImage(tileArray[i, j].tileImage, j * 50, i * 50, 50, 50);
}
}
grap.DrawImage(player.movingObjectImage, player.xPos, player.yPos, 50, 50);
grap.DrawImage(enemyGoblin.movingObjectImage, enemyGoblin.xPos, enemyGoblin.yPos, 50, 50);
groundPictureBox.Image = bmp;
// grap.Dispose();
}
}
This is the Timer with a specific interval :
private void UpdateTimer_Tick(object sender, EventArgs e)
{
if(player.Update()==true) // true keydown event is fired
{
this.Invalidate();
}
label1.Text = lastFrameRate.ToString(); // for fps rate show
CalculateFrameRate(); // for fps rate show
}

Are you writing the tile implementation yourself? Probably the issue is that at every frame you're drawing all tiles.
2D engines with scrolling tiles should draw tiles on a larger sprite than the screen, then draw that sprite around which is a fast operation (you'd need to specify the language you're using so I can provide some hint on how to actually make that fast - basically an in video memory accelerated blit, but every language has it's way to make it happen)
when the border of this super-sprite is closer to the screen border than a threshold (usually half tile), the larger sprite is redrawn around the current position - but there is no need to draw all the tiles on this! start copying the supersprite on this recentered sprite and you only need to draw the tiles missing from the previous supersprite because of the offset.

As mentioned in the comments your concept is wrong. So here's just a simple summary of how to do this task:
Tile map is static
From functional point of view does not matter if player moves or the map but from performance point of view the number of tiles is hugely bigger then number of players so moving player is faster.
To achieve player centered or follow views you have to move the camera too.
rendering
Repainting every 1ms is insane and most likely impossible on nowadays computers if you got medium complexity of the scene. Human vision can't detect it anyway so there is no point in repainting more that 25-40 fps. The only reason for higher fps needs is to be synchronized with your monitor refreshing to avoid scan line artifacts (even LCD use scan lines refreshing). To have more fps then the refresh rate of your monitor is pointless (many fps players would oppose but our perception is what it is no matter what they say).
Anyway if your rendering took more then 1ms (which is more then likely) then your timer is screwed because it should be firing several times before the first handler even stops. That usually causes massive slowdowns due to synchronisation problems so the resulting fps is usually even smaller then the rendering engine could provide. So how to remedy that?
set timer interval to 20ms or more
add bool _redraw=false
And use it to redraw only when you need to repaint screen. So on any action like player movement, camera movement or turn, animation change set it to true
inside timer event handler call your repaint only if _redraw==true and set it to false afterwards.
This will boost performance a lot. even if your repaint will take more than the timer interval still this will be much much faster then your current approach.
To avoid flickering use Back buffering.
camera and clipping
Your map is most likely much bigger then the screen so there is no point to repaint all the tiles. You can look at the camera as a means to select the right part of your map. If your game does not use rotations then you need just position and may be zoom/scale. If you want rotations then 2D 3x3 homogeneous matrices are the way.
Let assume you got only position (no zoom or rotating) then you can use this transformations:
screen_x=world_x-camera_x
screen_y=world_y-camera_y
world_x=screen_x+camera_x
world_y=screen_y+camera_y
So camera is your camera view position, world is you tile position in map grid and screen is the position on screen. If you got indexes of your tile in map then just multiply them by tile size in pixels to obtain the world coordinates.
To select only visible tiles you need to obtain the corner positions of your screen, convert them into world coordinates, then into indexes in map and finally render only tiles inside rectangle that these points form in your map + some margin of error (for example render 1 tile enlarged rectangle in all directions). This way the rendering will be independent on your map size. This process is called clipping.
I strongly recommend to look at these related QAs:
Improving performance of click detection on a staggered column isometric grid
2D Diamond (isometric) map editor ... read the comments there !!!
The demo in the linked QAs use only GDI and direct pixel access to bitmaps in win32 form app so you can compare performance with your code (they should be similar) and tweak your code until it behaves as should.

Related

How to use the DXGI flip model in a Direct2D windowed app?

I have a Win32 non-game windowed app that uses a Direct2D device context/HWND render target to draw a window. Currently it uses a DXGI swap chain with the DXGI_SWAP_EFFECT_DISCARD swap effect.
Microsoft recommends using the new flip model swap effects, either DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL or DXGI_SWAP_EFFECT_FLIP_DISCARD. I'm interested in using them primarily because they would allow me to specify a list of dirty rects when calling Present1(), which should improve performance/power usage.
Simply changing the SwapEffect to either of the new flip model values produces a weird (but actually expected) result of drawing a black window each second frame, with artifacts of the previous frames visible onscreen.
So the question is: is it possible to use the new flip model swap effects in this situation, and if yes, how should things be set up?
Given that the app needs to draw the dirty rects into an otherwise valid buffer, it seems that a correct approach would involve maintaining two buffers with essentially the same content (one to draw into, and one to give to the DWM for composition), so not sure if it would be possible to achieve any performance gains this way in an app that doesn't redraw each frame completely. But perhaps I'm missing something important.
The swap chain is currently set up as follows:
swapChainDesc.Width = ...;
swapChainDesc.Height = ...;
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Stereo = false;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 1;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
swapChainDesc.Flags = 0;
EDIT 1
It turns out that DXGI_SWAP_EFFECT_DISCARD forces BufferCount to 1, so my initial value of 2 was somewhat misleading, as only one buffer is used. Source (3rd comment).
Also the docs for DXGI_SWAP_EFFECT say that UWP apps are forced into the flip model, so this should be a solvable problem.
There are two good ways to do it.
The first way is a little heavier on energy usage. You can draw your contents into an intermediate buffer/render texture, and copy it to swapchain just before every present. That way you can only actually render the parts that changed in your intermediate buffer, and not care about what the state of the swapchain is.
The second way is more complicated, but can yield optimal energy usage. Instead of using intermediate buffer and drawing only what changes since the last frame there, you draw directly into the swapchain buffer. For this to work correctly, you need to redraw not what changes between current and last frame, but between current and (current - BufferCount) frame. For instance:
Frame 1 - you draw a green rectancle at (200 x 200) with dimensions of (150 x 150). The dirty region is entire frame because it's the first frame.
Frame 2 - you draw a blue rectangle at (250 x 250) with dimensions of (50 x 50). The dirty region is (250, 250, 300, 300).
Frame 3 - you draw a red rectangle at (225 x 225) with dimensions of (50 x 50). The dirty region is (225, 225, 50, 50).
If your buffer count is 2, that means when you draw frame 3, you need to not only redraw the dirty region of (225, 225, 50, 50), but also the dirty region of (250, 250, 300, 300).

outlining text in processing

My goal is to obtain an outline of text that is 1 pixels wide.
It could look something like this: https://jsfiddle.net/Lk1ju9yw/
I can't think of a good way to go about this so I did the following (in pseudocode):
PImage img;
void setup() {
size(400, 400);
// use text() to write on the canvas
// initialize PImage img
// load pixels for canvas and img
// loop thru canvas pixels and look for contrast
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
// compare canvas pixels at x-y with its neighbors
// change respective pixel on PImage img so as not to disturb canvas
}
}
// update pixels and draw img over the canvas
img.updatePixels();
img(img, 0, 0);
}
In a nutshell, I wrote white text on a black background on the canvas, did some edge detection and drew the results on a PImage, then used the PImage to store the results. I guess I could have skipped the PImage phase but I wanted to see what the edge detection algorithm produced.
So this does a decent job of getting the outline but there are some problems:
The outline is sometimes 1+ pixels wide. This is a problem. Suppose I want to store the outline (ie. all the positions of the white pixels) in an ArrayList.
For example, if using the ArrayList I draw an ellipse at EVERY point along the outline, the result is ok. But if I want the ellipses spaced apart, the ellipse-outline becomes kind of rough. In the fiddle I provided, the left edge of the letter 'h' is 2 pixels wide. Sometimes the ellipse will be drawn at the inner pixel, sometimes at the outer. That kind of thing makes it look ugly.
Elements of the ArrayList might be neighbors in the ArrayList, but not on the PImage. If I want to draw a circle for every 10th ArrayList location, the result won't necessarily be spaced apart on the PImage.
Here is an example of how ugly it can be: https://jsfiddle.net/Lk1ju9yw/1/
I am quite sure I understand why this is happening. I just don't know how to avoid it.
I also believe there is a solution (a PFont method) in p5.js. I am comfortable using p5 but unless I have to (let's say, because of difficulty), I would rather use processing. I've also heard of some libraries in processing that can help with this. Partly, I am interested in the result, but I am also interested in learning if I can program a solution myself (with some guidance, that is).
You can get an outline of text very easily in P5.js, because text honors the fill and stroke colors. So if you call noFill() the text will not be filled in, and if you call stroke(0) the text will have a black outline.
function setup() {
createCanvas(400, 200);
noSmooth();
}
function draw() {
background(220);
textSize(72);
textAlign(CENTER);
noFill();
stroke(0);
text("hey", width/2, height/2);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js"></script>
Unfortunately this approach won't work in regular Processing, because it just uses the stroke color for text. I'm not totally sure about Processing.js, but my guess is it's the same as Processing.
If you draw this to a buffer (using createGraphics()), then you can iterate over the buffer to get a list of points that make up your outline.
Now, as for putting the points in the correct order, you're going to have to do that yourself. The first approach that occurs to me is to sort them and group them by letter.
For example, your algorithm might be:
Find the upper-left-most point. Add it to your list.
Does that point you just added have any neighbors? If so, pick one and add it to your list. Repeat this step until the point has no neighbors.
Are there any points left? If so, find the point closest to the one you just added, and add it to your list. Go to step 2.
This might not be perfect, but if you want something more advanced you might have to start thinking about processing the list of points: maybe removing points that have a left neighbor, for example. You're going to have to play around to find the effect you're looking for.
This was an interesting question., thanks for that. Good luck, sounds like a fun project.

how can I implement a slow smooth background scrolling in sdl

I am trying to implement background scrolling using SDL 2.
As far as I understand one can only move source rectangle by an integer value.
My scrolling works fine when I move it by one every iteration of the game loop.
But I want to move it slower. I tried to move it using this code
moved += speed;
if (moved >= 1.0) {
++src_rect.x;
moved -= 1;
}
Here moved and speed are doubles . I want my background to move something like ten times slower, therefore I set speed to 0.1. It does move ten times slower, but the animation is no longer smooth. It kind of jumps from one pixel to another, which looks and feels ugly when the speed is low.
I am thinking of making my background larger and scrolling it using an integer. Maybe when background is large enough the speed of 1 will seem slower.
Is there a way to scroll not a very large background slowly and smoothly and the same time?
Thanks.
What I would do is have a set of floats that would track the virtual screen position, then you just cast the floats to integers when you actually render, that way you don't ever lose the precision of the floats.
To give you an example, I have an SDL_Rect, I want to move it every frame. I have two floating point variables that track the x and y position of the rect, every frame I would update those x and y positions, cast them to an integer, and then render the rect, EX:
// Rect position
float XPos = 0.0f;
float YPos = 0.0f;
SDL_Rect rect = {0, 0, 64, 64};
// Update virtual positions
XPos += 20.0f * DeltaTime;
YPos += 20.0f * DeltaTime;
// Move rect down and to the right
rect.x = (int)XPos;
rect.y = (int)YPos;
While this doesn't give you the exact result you are wanting, it is the only way that I know of to do this, it will let you delay your movement more precisely without giving you that ugly chunkiness in the movement, it also will let you add stuff like more precise acceleration too. Hope this helps.

Invisible, interactable objects in AS3 -- how to code efficient invisibility?

Alpha invisibility.
I currently define circular regions on some images as "hot spots". For instance, I could have my photo on screen and overlay a circle on my head. To check for interaction with my head in realtime, I would returnOverlaps and do some manipulation on all objects overlapping the circle. For debugging, I make the circle yellow with alpha 0.5, and for release I decrease alpha to 0, making the circle invisible (as it should be).
Does this slow down the program? Is there another way to make the circle itself invisible while still remaining capable of interaction? Is there some way to color it "invisible" without using a (potentially) costly alpha of 0? Cache as bitmap matrix? Or some other efficient way to solve the "hot spot" detection without using masks?
Having just a few invisible display objects should not slow it down that much, but having many could. I think a more cleaner option may be to just handle it all in code, rather then have actual invisible display objects on the stage.
For a circle, you would define the center point and radius. Then to get if anyone clicked on it, you could go:
var xDist:Number = circle.x - mousePoint.x;
var yDist:Number = circle.y - mousePoint.y;
if((xDist * xDist) + (yDist * yDist) <= (circle.radius * circle.radius)){
// mousePoint is within circle
} else {
// mousePoint is outside of circle
}
If you insist on using display objects to set these circular hit areas (sometimes it can be easier visually, then by numbers), you could also write some code to read those display objects (and remove them from being rendered) in to get their positions and radius size.
added method:
// inputX and inputY are the hotspot's x and y positions, and inputRadius is the radius of the hotspot
function hitTestObj(inputA:DisplayObject, inputX:int, inputY:int, inputRadius:int):Boolean {
var xDist:Number = inputX - inputA.x;
var yDist:Number = inputY - inputA.y;
var minDist:Number = inputRadius + (inputA.width / 2);
return (((xDist * xDist) + (yDist * yDist)) =< (minDist * minDist))
}
An alpha=0 isn't all that costly in terms of rendering as Flash player will optimize for that (check here for actual figures). Bitmap caching wouldn't be of any help as the sprite is invisible. There's other ways to perform collision detection by doing the math yourself (more relevant in games with tens or even hundreds of sprites) but that would be an overkill in your case.

Rotating a single image (but not the others) on an html5 canvas?

I have a sprite I'm animating on an html canvas using normal sprite sheet blitting. On certain key events I'd like to change the direction of the sprite (ie, flip it, or rotate it 180 degrees) without changing anything (the other sprites) on the canvas.
Does anyone know how to do this?
So I was having this issue with my game; I had cells for up, down, and left animations, but no right cells. So I needed to flip the left cells to draw the right cells.
For each sprite I keep track of it's current top and left in the canvas, as well as each cell's top and left in the sprite sheet.
I've seen previous answers showing a simple horizontal flip as just translating the origin and flipping (inverse scale) of the axes, BUT this does not take into account that with sprites, flipping the origin will mess up the sprite's registration point (its top and left on the canvas).
This issue manifested in the sprite being mirrored correctly, but it's position being off by the width of the sprite. I solved it by taking into account the width of the sprite. Notice how I'm using CanvasRenderingContext2D.prototype.drawImage with 9 arguments since I'm slicing a sprite out of a sprite sheet:
// check if we need to flip image (special case for right movement)
if(sprite.translated){
context.save();
context.translate(context.canvas.width, 0);
context.scale(-1, 1);
context.drawImage(sprite.sheet,
cell.left,
cell.top,
sprite.width,
sprite.height,
// secret sauce: change the destination's X registration point
context.canvas.width - sprite.left - sprite.width,
sprite.top,
sprite.width, sprite.height);
context.restore();
} else {
// Assumes cells are all the same width and height, set in sprite
context.drawImage(sprite.sheet, cell.left, cell.top, sprite.width,
sprite.height, sprite.left, sprite.top, sprite.width, sprite.height);
}
Note: I also could have done the math in the translate, since it's meant to simplify calculations elsewhere.
Simply redraw the sprite, with a rotate transformation. Transformations in HTML Canvas 2D Context
The canvas is just an off-screen buffer. It won't be cleared unless you tell it to, and nothing else will be changed unless you tell it to.
There's a bunch of different situations in which you may have to redraw the area of or around the sprite. Otherwise, you'll get a ghosting effect where part of the old sprite is still visible below the new drawing, or other drawings become obscured. Some reasons are:
Your sprite is partially transparent,
Your sprite is partially translucent,
Other drawings are made on top of your sprite,
Your sprite is non-rectangular,
You're doing flips that are not multiples of 90 degrees.
So that might be a bit more work, and there are several different approaches to doing that. You could simply redraw the entire scene, or just the specific objects at the location, perhaps using the clip method.
A completely different direction might be to use other HTML elements, img or div, with absolute positioning and CSS3 transformations. That's basically a bit of trickery to delegate the rendering of your scene to the browser.
While I appreciate Shtééf's answer, after a bit of research, I have found that rotating the canvas you are actually using to display doesn't seem to be ideal. The saving, rotating and restoring while trying to create complex animations (aka think Street Fighter 2 not astroids) causes the canvas to flicker in even Chrome.
I have found however a usable strategy. The idea here is that you actually create two canvases, one will be for your game and the other will be a backbuffer of sorts and it will be used to rotate or scale your sprites. You essentially transform the backbuffer canvas, draw the image in question, then transfer it to your main canvas and restore (or not) the backbuffer. In this manner, you only rotate the hidden canvas and only effect the sprite in question not the entire game board.
The code looks something like this (work in progress):
mainContext.clearRect(lastXpos, lastYpos, lastWidth, lastHeight);
backContext.clearRect(0, 0, lastWidth, lastHeight);
lastXpos = xpos;
lastYpos = ypos;
lastWidth = width;
lastHeight = height;
backContext.save();
//check the direction of the sprite
//turn the backContext to this direction
//SPRITE_INVERTED==-1
if (spriteXDirection == SPRITE_INVERTED || spriteYDirection == SPRITE_INVERTED)
{
var horScale = 0;
var verScale = 0;
if (spriteXDirection == SPRITE_INVERTED)
{
horScale = width;
}
if (spriteYDirection == SPRITE_INVERTED)
{
verScale = height;
}
backContext.translate(horScale, verScale);
backContext.scale(spriteXDirection, spriteYDirection);
}
//draw the sprite not we always use 0,0 for top/left
backContext.drawImage(animations[currentPlay].sheet,
animationX,
animationY,
width,
height, 0, 0, width, height);
//Get the image data from the back context
var image = backContext.getImageData(0, 0, width, height);
//flip the back context back to center - or not, I haven't decided how to optimize this yet.
backContext.restore();
//transfer the image to your main context
mainContext.putImageData(image, xpos, ypos);
This has saved me a lot of headaches in understanding how to translate my sprites without having everything on my gameboard move all over the place. It also seems to perform better then modifying the main context.
Why don't you use save() and restore
ctx.save(); // save current state
ctx.rotate(Math.PI); // rotate
ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
ctx.restore(); // restore original states (no rotation etc)
How to rotate one image in a canvas?

Resources