C++ - How to play animation in opposite direction Cocos2DX - c++

I got a png like this
I also got this segment of code
SpriteFrameCache::getInstance()->addSpriteFramesWithFile("walk.plist", "walk.png");
Vector<SpriteFrame*> animFrames;
animFrames.reserve(8);
char spriteFrameByName[MAX_WORD] = { 0 };
for (int index = 1; index <= 8; index++)
{
sprintf(spriteFrameByName, "%d.png", index);
auto frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(spriteFrameByName);
animFrames.pushBack(frame);
}
Animation* animation = Animation::createWithSpriteFrames(animFrames, time);
sprite->runAction(Animate::create(animation));
Now I want to horizontally flip this animation. Something looks like this
Not to create another png file, is there a way to this in C++ code?

Animation* animation = Animation::createWithSpriteFrames(animFrames, time);
sprite->runAction(Animate::create(animation));
sprite->setFlipX(true)

Horizontally flipping an image is equal to scaling the x-axis of that image minus 1. I am not familiar with Cocos2DX, but multiplying the x scale of your image by -1 will horizontally flip it for you.
This answer might help you with scaling:

I am not entirely sure if flip function handles the rotation of the object you want to flip. I believe that it only changes the texture's direction. Which may end up making things a bit more complex down the road if you ever need to compute which direction is your character is facing in your game world.
You can rotate the entire sprite on Y axis. by doing so, It will make sure that everything facing in right direction and not just the texture. Following code provides the same visual by rotating entire sprite.
sprite->setRotation3D(Vec3(0, 180, 0));

Related

Spritesheet animation with scaled frames

In order to create an animation in cocos2d-x 3.2 I do this:
SpriteFrameCache* cache = SpriteFrameCache::getInstance();
Vector<SpriteFrame*> animFrames(15);
for(int i = 1; i <= 7; ++i)
{
SpriteFrame* frame = cache->getSpriteFrameByName(String::createWithFormat("%d.png", i)->getCString());
animFrames.pushBack(frame);
}
auto animation = Animation::createWithSpriteFrames(animFrames, 1 / animFrames.size());
auto animate = Animate::create(animation);
pSprite->runAction(animate);
But now I need some frames to scaleByX with -1 in order to create a mirrored image. SpriteFrame has not scale method. Also I can't scale the pSprite as only some of the frames should be scaled. How can I solve this problem?
You have a pretty weird situation :)
You can schedule an update selector on sprite and set flipX to true/false based on your desired conditions. That's my personal preference.
You can't hack SpriteFrame that way, but you can use RenderTexture: http://www.cocos2d-x.org/reference/native-cpp/V3.0alpha0/d9/ddc/classcocos2d_1_1_render_texture.html - flip your desired sprites in a new texture, basically generate a new sprite-sheet on the fly. Now that's a bad idea.

SDL Screen Render Position Error Along Top of Window

I am making a top down isometric game using SDL 2.0 and C++ and have come across a glitch.
When a texture is rendered to the screen using the SDL_RenderCopyfunction, the moment the top of the texture hits the top of the screen it gets pushed down by one pixel, thus causing the missing borders seen in the following picture:
Pre-edit with no annotations
Post-edit with with annotations
The following is my render function specific to the world itself, as the world renders differently from everything else in the game, because I am simply copying a "source" texture instead of loading a texture for every single tile in the game, which would be absurdly inefficient.
//-----------------------------------------------------------------------------
// Rendering
DSDataTypes::Sint32 World::Render()
{
//TODO: Change from indexing to using an interator (pointer) for efficiency
for(int index = 0; index < static_cast<int>(mWorldSize.mX * mWorldSize.mY); ++index)
{
const int kTileType = static_cast<int>(mpTilesList[index].GetType());
//Translate the world so that when camera panning occurs the objects in the world will all be in the accurate position
I am also incorporating camera panning as follows (paraphrased with some snippets of code included as my camera panning logic spans multiple files due to the object orientated design of my game):
(code from above immediately continued below)
mpTilesList[index].SetRenderOffset(Window::GetPanOffset());
//position (dstRect)
SDL_Rect position;
position.x = static_cast<int>(mpTilesList[index].GetPositionCurrent().mX + Window::GetPanOffset().mX);
position.y = static_cast<int>(mpTilesList[index].GetPositionCurrent().mY + Window::GetPanOffset().mY);
position.w = static_cast<int>(mpTilesList[index].GetSize().mX);
position.h = static_cast<int>(mpTilesList[index].GetSize().mY);
//clip (frame)
SDL_Rect clip;
clip.x = static_cast<int>(mpSourceList[kTileType].GetFramePos().mX);
clip.y = static_cast<int>(mpSourceList[kTileType].GetFramePos().mY);
clip.w = static_cast<int>(mpSourceList[kTileType].GetFrameSize().mX);
clip.h = static_cast<int>(mpSourceList[kTileType].GetFrameSize().mY);
I am confused as to why this is happening, as regardless of whether I include my simple culling algorithm or not (as shown below), the same result occurs.
(code from above immediately continued below)
//Check to ensure tile is being drawn within the screen size. If so, rendercopy it, else simply skip over and do not render it.
//If the tile's position.x is greather than the left border of the screen
if(position.x > (-mpSourceList[kTileType].GetRenderSize().mX))
{
//If the tile's position.y is greather than the top border of the screen
if(position.y > (-mpSourceList[kTileType].GetRenderSize().mY))
{
//If the tile's position.x is less than the right border of the screen
if(position.x < Window::msWindowSize.w)
{
//If the tile's position.y is less than the bottom border of the screen
if(position.y < Window::msWindowSize.h)
{
SDL_RenderCopy(Window::mspRenderer.get(), mpSourceList[kTileType].GetTexture(), &clip, &position);
}
}
}
}
}
return 0;//TODO
}
You may have a rounding error when you are casting the positions to ints. Perhaps you should round to the nearest integer instead of just taking the floor (which is what you're cast is doing). A tile at position (0.8, 0.8) will be rendered at pixel (0, 0) when it should probably be rendered at position (1, 1).
Or you could ensure that the size of your tiles is always an integer, then errors shouldn't accumulate.
Short version of answer, restated at bottom:
By fixing my data types issue, that allowed me to fix my math library, which removed the issue of rounding parts of pixels, since there is no such thing as less than 1 but greater than 0 pixels on the screen when rendering.
Long Answer:
I believe the issue to have been caused by a rounding error with the offset logic used when rotating a 2D grid to a diagonal isometric perspective, with the rounding error only occurring when dealing with screen coordinates between -1 and +1.
Since I based the conversion from changing an orthogonal grid to a diagonal grid on the y axis (rows), this would explain why the single pixel offset was occurring only at the top border of the screen and not bottom border.
Even though every single row had implicit rounding occurring without any safety checks, only the conversion from world coordinates to screen coordinates dealt with rounding between a positive and negative number.
The reason behind all of this is because my math library which was templatized had an issue a lot of my code being based on type defined user types such as:
typedef unsigned int Uint32;
typedef signed int Sint32;
So I simply used DSMathematics::Vector2<float> instead of the proper implementation of DSMathematics::Vector2<int>.
The reason this is an issue is because there cannot be "half a pixel" on the screen, and thus integers must be used instead of floating point values.
By fixing my data types issue, that allowed me to fix my math library, which removed the issue of rounding parts of pixels, since there is no such thing as less than 1 but greater than 0 pixels on the screen when rendering.

SDL and c++ -- More efficient way of leaving a trail behind the player?

so i'm fairly new with SDL, and i'm trying to make a little snowboarding game. When the player is moving down the hill, I want to leave a trail of off-coloured snow behind him. Currently, the way i have this working is I have an array (with 1000 elements) that stores the players last position. Then each frame, I have a for loop that loops 1000 times, to draw out the trail texture in all these last 1000 positions of the player...
I feel this is extremely inefficient, and i'm looking for some better alternatives!
The Code:
void Player::draw()
{
if (posIndex >= 1000)
{
posIndex = 0;
}
for (int i = 0; i < 1000; i++) // Loop through all the 1000 past positions of the player
{
// pastPlayerPos is an array of SDL_Rects that stores the players last 1000 positions
// This line calculates teh location to draw the trail texture
SDL_Rect trailRect = {pastPlayerPos[i].x, pastPlayerPos[i].y, 32, 8};
// This draws the trail texture
SDL_RenderCopy(Renderer, Images[IMAGE_TRAIL], NULL, &trailRect);
}
// This draws the player
SDL_Rect drawRect = {(int)x, (int)y, 32, 32};
SDL_RenderCopy(Renderer, Images[0], NULL, &drawRect);
// This is storing the past position
SDL_Rect tempRect = {x, y, 0, 0};
pastPlayerPos[posIndex] = tempRect;
posIndex++; // This is to cycle through the array to store the new position
This is the result, which is exactly what i'm trying to accomplish, but i'm just looking for a more efficient way. If there isn't one, i will stick with this.
There are multiple solutions. I'll give you two.
1.
Create screen-size surface. Fill it with alpha. On each player move, draw it's current position into this surface - so each movement will add you extra data to this would-be mask. Then blit this surface on screen (beware of blit order). In your case it could be improved by disabling alpha and initially filling surface with white, and blitting it first, before anything else. With that approach you can skip screen clearing after flip, by the way.
I recommend starting with this one.
2.
Not easy one, but may be more efficient (it depends). Save array points where player actually changed movement direction. After it, you need to draw chainline between these points. There is however no builtin functions in SDL to draw lines; maybe there are in SDL_gfx, i never tried it. This approach may be better if you'll use OpenGL backend later on; with SDL (or any other ordinary 2D drawing library), it's not too useful.

How to clear a part of screen in SDL 2.0

So, currently i'm writing a game and i have a small texture(20x20) that fills screen (1680x1050). My player moves on this backgroud, all in game loop. I need my backgound to be static and drawn just once, but SDL_RenderClear redraws all the area and this causes to lag. How can i draw it once, and then update it with my player figure?
There is really no way to "Draw it once and leave it like that"
But there is a way you can make just a part of to draw in order to have the same result, just draw the part of the background that the character is, before drawing the new character.
In more details, draw all the "blocks" that are touched by the hero even by a little bit, then draw your hero over them.
Here is an example:
//LocationX is the location of hero on the X axis
//LocationX /20 is the number of the first texture that is drawn on X axis
//LocationX +Width (width of hero) +20 (width of texture) this is the number of the last texture on X axis
//Now draw everything from first to last texture that is touched the hero (just calculate the Y axis the same way as X axis!)
for (int xxx = LocationX /20; xxx < LocationX +Width + 20; xxx++)
{
for (/*Do the same for Y axis*/)
{
draw(texture, xxx *20, yyy *20);
}
}
//Draw Hero here, the background is clear!
Literally answering, you should use SDL_RenderDrawRect to 'clear a part of screen'.
However, you've mentioned that you have texture at background - RenderClear wouldn't draw a texture, so something in this description appears to be wrong.

2D tile based game, shows gaps between the tile sprites when I zoom in with the camera?

I am using the D3DXSPRITE method to draw my map tiles to the screen, i just added a zoom function which zooms in when you hold the up arrow, but noticed you can now see gaps between the tiles, here's some screen shots
normal size (32x32) per tile
zoomed in (you can see white gaps between the tiles)
zoomed out (even worst!)
Here's the code snipplet which I translate and scale the world with.
D3DXMATRIX matScale, matPos;
D3DXMatrixScaling(&matScale, zoom_, zoom_, 0.0f);
D3DXMatrixTranslation(&matPos, xpos_, ypos_, 0.0f);
device_->SetTransform(D3DTS_WORLD, &(matPos * matScale));
And this is my drawing of the map, (tiles are in a vector of a vector of tiles.. and I haven't done culling yet)
LayerInfo *p_linfo = NULL;
RECT rect = {0};
D3DXVECTOR3 pos;
pos.x = 0.0f;
pos.y = 0.0f;
pos.z = 0.0f;
for (short y = 0;
y < BottomTile(); ++y)
{
for (short x = 0;
x < RightTile(); ++x)
{
for (int i = 0; i < TILE_LAYER_COUNT; ++i)
{
p_linfo = tile_grid_[y][x].Layer(i);
if (p_linfo->Visible())
{
p_linfo->GetTextureRect(&rect);
sprite_batch->Draw(
p_engine_->GetTexture(p_linfo->texture_id),
&rect, NULL, &pos, 0xFFFFFFFF);
}
}
pos.x += p_engine_->TileWidth();
}
pos.x = 0;
pos.y += p_engine_->TileHeight();
}
Your texture indices are wrong. 0,0,32,32 is not the correct value- it should be 0,0,31,31. A zero-based index into your texture atlas of 256 pixels would yield values of 0 to 255, not 0 to 256, and a 32x32 texture should yield 0,0,31,31. In this case, the colour of the incorrect pixels depends on the colour of the next texture along the right and the bottom.
That's the problem of magnification and minification. Your textures should have invisible border populated with part of adjacent texture. Then magnification and minification filters will use that border to calculate color of edge pixels rather than default (white) color.
I think so.
I also had a similar problem with texture mapping. What worked for me was changing the texture address mode in the sampler state description; texture address mode is used to control what direct3d does with texture coordinates outside of the ([0.0f, 1.0f]) range: i changed the ADDRESS_U, ADDRESS_V, ADDRESS_W members to D3D11_TEXTURE_ADDRESS_CLAMP which basically clamps all out-of-range values for the texture coordinates into the [0.0f, 1.0f] range.
After a long time searching and testing people solutions I found this rules are the most complete rules that I've ever read.
pixel-perfect-2d from Official Unity WebSite
plus with my own experience i found out that if sprite PPI is 72(for example), you should try to use more PPI for that Image(96 maybe or more).It actually make sprite more dense and make no space for white gaps to show up.
Welcome to the world of floating-point. Those gaps exist due to imperfections using floating-point numbers.
You might be able to improve the situation by being really careful when doing your floating-point math but those seams will be there unless you make one whole mesh out of your terrain.
It's the rasterizer that given the view and projection matrix as well as the vertex positions is slightly off. You maybe able to improve on that but I don't know how successful you'll be.
Instead of drawing different quads you can index only the visible vertexes that make up your terrain and instead use texture tiling techniques to paint different stuff on there. I believe that won't get you the ugly seam because in that case, there technically isn't one.