Why sprites tremble in cocos2d-x - c++

I create a little game on cocos2d-x and have some problem in mobile version. Game have layer with terrain and character and layer with ui/info objects. Layer with terrain does not move. And layer with ui/info move with character (so it static on screen).
In mobile version all sprites from ui layer are trembling, but only sprites, labels are static. In PC version sprites and labels are also static.
Create label and sprite. Label static on PC (Win and Mac) and mobile (Android), sprite static on PC and tremble on mobile:
auto infoLayer = m_params->getGameInfoDelegate(); // class GameInfo
auto size = Director::getInstance()->getVisibleSize();
TTFConfig ttfconfig("fonts/Marker Felt.ttf", 100);
auto label = Label::createWithTTF(ttfconfig, "0");
label->setPosition(Vec2(size.width / 2, size.height / 2 + 40));
label->setString("Hello");
infoLayer->getLayer()->addChild(label, 10);
auto spr = Sprite::create();
spr->setColor(Color3B(200, 100, 100));
spr->setTextureRect(Rect(0, 0, 150, 150));
spr->setPosition(Vec2(size.width / 2, size.height / 2 - 40));
infoLayer->getLayer()->addChild(spr, 9);
Update position layer and camera:
update(float t)
{
...
m_cameraFollow->update();
...
}
void CameraFollow::update()
{
float moveX;
float moveY;
...
m_camera->move(Vec2(moveX, moveY)); // class GameCamera
}
void GameCamera::move(const cocos2d::Vec2& m)
{
float x;
float y;
...
m_position.x = x;
m_position.y = y;
m_camera->setPosition(m_position); // class cocos2d::Camera
auto infoPanel = m_params->getGameInfoDelegate(); // class GameInfo
if(infoPanel)
{
infoPanel->setMoving(m_position - m_startPosition);
}
}
class GameInfo : public cocos2d::Layer, public GameInfoDelegate
void GameInfo::setMoving(const cocos2d::Vec2 &position)
{
this->setPosition(position);
}
So, how i can fix it?

The answer to your question is complicated. The main reason is that your phone does not have the same processing power as your computer, and Cocos2d-x uses some clever optimizations to try and hide that. With moving sprites, it has to redraw them every frame (usually 30-60 fps), and slight inconsistencies can lead to this effect.
To remedy this, I would double check that your fps is 60, because 30 fps will lead to trembling. Also, if you're updating the sprite's position in update(float dt), I would try and use the physics engine instead, with velocities. If that isn't an option, maybe try to have less layers, because the more sprites you draw ontop of one another, the more it will look like it is jittering. Let me know if any of these solutions work.

The issue may be related to how you are moving the camera. Setting new X,Y coordinates through your update method without factoring in the delta time on each update call will result in jerky movement on screen.
You need to smooth out the movement from one location to another.
Try this:
update(float dt)
{
...
m_cameraFollow->update(dt);
...
}
void CameraFollow::update(float dt)
{
float moveX;
float moveY;
float speed = 1.0f;
...
Vec2 cameraPosition = m_camera->getPosition();
Vec2 targetPosition = Vec2(moveX, moveY);
Vec2 newPosition = cameraPosition.lerp(targetPosition, dt * speed);
m_camera->move(newPosition);
}

Related

How to make player animation in sfml?

i wanted to make player a bit realistic instead of just hovering around like a ghost. this is my code just some basic gravity and movement, i want to make jumping animation, walking animation, turn left and turn right animation. how do i do that?
here code:
void Game::initTexture()
{
if (!texture.loadFromFile("/home/webmaster/Documents/vscode-sfml/src/Ball_and_Chain_Bot/pixil-frame-0(1).png"))
{
std::cout << "failed to load texture\n";
rect.setTexture(texture);
rect.setPosition(this->window->getView().getCenter());
rect.setScale(2,2);
}
}
rendering:
void Game::render()
{
this->window->clear(sf::Color(239, 235, 216));
this->window->draw(rect);
this->window->display();
}
You forgot to show your actual code, the headers are not very useful. Regardless:
Define some animations. Each animation should have a target length and a number of frames that cover that target. At the beginning it is easiest to make every frame be equally long, but nobody stops you from having frame 1 take 0.2s, frame 2 0.8s, and frame 3 0.15s.
add code to keep track of a "current animation" that properly cycles through the frames on the proper timescale (ie show each frame for 0.25s if you have 4 frames and a target of 1s). Some animations may cycle, such as the "running" or "idle" animation. A common technique for storing animations is a "texture atlas" that contains all frames of an animation. You can then use sf::Shape::setTextureRect to select a part of the texture to draw.
update your movement and input code to change the animation if the state of the character changes
Let us define the frames of an animation in terms of sf::IntRect sections of a given sprite sheet: (example sprite sheet)
std::vector<sf::IntRect> idle {
{0, 0, 35, 61}
};
std::vector<sf::IntRect> runningRight {
{657, 473, 43, 52}, // first frame is at 657x473 and is 43x52 pixels
// other frames.
};
We can define an Animation class with the following data and methods:
class Animation {
public:
Animation(const std::vector<sf::IntRect>& frames, float duration, bool cycles = true) : frames(frames), frameTime(duration / frames.size()), cycles(cycles) { reset(); }
void reset() {
currentFrame = 0;
currentFrameTime = 0;
}
void update(float dt);
const sf::IntRect& getCurrentRect() const { return frames[currentFrame]; }
private:
const std::vector<sf::IntRect>& frames;
const float frameTime;
bool cycles;
int currentFrame;
float currentFrameTime;
};
This implements most of step 2: keeping track of which frame should be on screen, assuming update(dt) is called every frame.
Now all that remains is the update method:
void Animation::update(float dt) {
currentFrameTime += dt;
// TODO: take `cycles` into account.
while (currentFrameTime >= frameTime) {
currentFrameTime -= frameTime;
currentFrame = (currentFrame + 1) % frames.size();
}
}
Finally, to hook this up, create the following variables:
sf::Texture textureAtlas = ...;
Animation currentAnimation{idle, 10.0f};
sf::Sprite player(textureAtlas, currentAnimation.getCurrentRect());
In your game's update() code, call currentAnimation.update(dt).
In the render function, make sure to call player.setTextureRect(currentAnimation.getCurrentRect()).
If you receive input, do something like currentAnimation = Animation{runningRight, 1.0f};

Light shader moved while resizing window

I've been working around to make a little light shader.
It works perfectly, I mean, the light fades as it's supposed to, it's a circle around my character moving with it.
It could be perfect only if that resizing event wasn't existing.
When SFML resizes the window, it enlarges everything, but in a strange way. It enlarged everything but shaders.
I tried to resize my window (I love resizing pixel graph games, I find it most beautiful. So I don't want to prevent the resizing event).
Here's my shader :
uniform vec3 light;
void main(void) {
float distance = sqrt(pow(gl_FragCoord.x - light.x, 2) + pow(gl_FragCoord.y - light.y, 2));
float alpha = 1.;
if (distance <= light.z) {
alpha = (1.0 / light.z) * distance;
}
gl_FragColor = vec4(0., 0., 0., alpha);
}
So, the problem is, my window is showed at 1280 x 736 (to fit with 32x32 textures), and I have a 1920 x 1080 monitor. When I enlarge the window to fit in 1920 x 1080 (title bar included), the whole thing resizes correctly, everything's fine, but the shader is now 1920x1080 (minus the title bar). So the shader needs different coordinates (what's supposed to be in x = 32, y = 0 is, for the shader, in x = 48 y = 0).
So I was wondering, is it possible to enlarge the shader with the whole window ? Should I use events or something like that ?
Thanks for your answers ^^
EDIT : Here's some pics :
So this is the light shader before it resizes (it's dark everywhere but on the player, like it's supposed to be).
Then I resize the window, the player doesn't move, the textures fit the entire window, but the light moved.
So, to explain correctly, when I resize the window, I want everything to fit the window, so it's full of textures, but when I do that, the coordinates given to my shader are the ones before resizing, and if I move it moves as if I didn't resize the window, so the light is never on my player again.
I'm not sure it's clearer, but I tried my best.
EDIT2 : Here's my code which calls the shader :
void Graphics::UpdateLight() {
short radius = 65; // 265 on the pictures
int x = m_game->GetPlayer()->GetSprite()->getPosition().x + CASE_LEN / 2; // Setting on the middle of the player sprite (CASE_LEN is a const which contains the size of a case (here 32))
int y = HEIGHT - (m_game->GetPlayer()->GetSprite()->getPosition().y + CASE_LEN / 2); // (the "HEIGHT -" part was set because it seems that y = 0 is on the bottom of the texture for GLSL)
sf::Vector3f shaderLight;
shaderLight.x = x;
shaderLight.y = y;
shaderLight.z = radius;
m_lightShader.setParameter("light", shaderLight);
}
The code snippet you're showing really only updates the shader coordinates (and from a quick glimpse it looks fine). The bug most likely happens somewhere where you're actually drawing things.
I'd use a completely different approach, because your shader approach might get rather tedious once you're rendering multiple things, other light sources, etc.
As such I'd suggest you render a light map to a render texture (which would essentially be like "black = no light, color = light of that color").
Rather than trying to explain everything in text, I've written a quick commented example program which will draw a window on screen and move some light sources over a background image (I've used the one that comes with SFML's shader example):
There are no requirements other than having a file called "background.jpg" in your startup path.
Feel free to copy this code or use it for inspiration. Just keep in mind this isn't optimized and really just a quick edit to show the general idea.
#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>
const float PI = 3.1415f;
struct Light
{
sf::Vector2f position;
sf::Color color;
float radius;
};
int main()
{
// Let's setup a window
sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Lights");
window.setVerticalSyncEnabled(false);
window.setFramerateLimit(60);
// Create something simple to draw
sf::Texture texture;
texture.loadFromFile("background.jpg");
sf::Sprite background(texture);
// Setup everything for the lightmap
sf::RenderTexture lightmapTex;
// We're using a 512x512 render texture for max. compatibility
// On modern hardware it could match the window resolution of course
lightmapTex.create(512, 512);
sf::Sprite lightmap(lightmapTex.getTexture());
// Scale the sprite to fill the window
lightmap.setScale(640 / 512.f, 480 / 512.f);
// Set the lightmap's view to the same as the window
lightmapTex.setView(window.getDefaultView());
// Drawable helper to draw lights
// We'll just have to adjust the first vertex's color to tint it
sf::VertexArray light(sf::PrimitiveType::TriangleFan);
light.append({sf::Vector2f(0, 0), sf::Color::White});
// This is inaccurate, but for demo purposes…
// This could be more elaborate to allow better graduation etc.
for (float i = 0; i <= 2 * PI; i += PI * .125f)
light.append({sf::Vector2f(std::sin(i), std::cos(i)), sf::Color::Transparent});
// Setup some lights
std::vector<Light> lights;
lights.push_back({sf::Vector2f(50.f, 50.f), sf::Color::White, 100.f });
lights.push_back({sf::Vector2f(350.f, 150.f), sf::Color::Red, 150.f });
lights.push_back({sf::Vector2f(150.f, 250.f), sf::Color::Yellow, 200.f });
lights.push_back({sf::Vector2f(250.f, 450.f), sf::Color::Cyan, 100.f });
// RenderStates helper to transform and draw lights
sf::RenderStates rs(sf::BlendAdd);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
}
}
bool flip = false; // simple toggle to animate differently
// Draw the light map
lightmapTex.clear(sf::Color::Black);
for(Light &l : lights)
{
// Apply all light attributes and render it
// Reset the transformation
rs.transform = sf::Transform::Identity;
// Move the light
rs.transform.translate(l.position);
// And scale it (this could be animated to create flicker)
rs.transform.scale(l.radius, l.radius);
// Adjust the light color (first vertex)
light[0].color = l.color;
// Draw the light
lightmapTex.draw(light, rs);
// To make things a bit more interesting
// We're moving the lights
l.position.x += flip ? 2 : -2;
flip = !flip;
if (l.position.x > 640)
l.position.x -= 640;
else if (l.position.x < 0)
l.position.x += 640;
}
lightmapTex.display();
window.clear(sf::Color::White);
// Draw the background / game
window.draw(background);
// Draw the lightmap
window.draw(lightmap, sf::BlendMultiply);
window.display();
}
}

The collision in SFML is not that good, how to improve it?

I've been lately working on a simple game using C++ and SFML latest version, but I had a problem which is that the collision detection is not that good, for example the player dies even if the enemy didn't touch him yet, but just near him. Here is the code of the player class with the move function and collision detection code AND the moves of the enemy class:
`class PlayerA : public CircleShape
{
public:
//Constructor:
PlayerA(float xposition, float yposition, float radius, float s)
{
setRadius(radius);
setFillColor(Color::Yellow);
setOutlineColor(Color(00,80,00));
setOutlineThickness(-2);
setPointCount(3);
setSpeed(s);
setPosition(xposition,yposition);
}
//Movements of the player:
void up()
{
move(0,-10*speed);
}
void down()
{
move(0,10*speed);
}
void right()
{
move(10*speed,0);
}
void left()
{
move(-10*speed,0);
}
void checkA(ObsA *obs1=NULL,ObsA *obs2=NULL, ObsA *obs3=NULL, ObsA *obs4=NULL, ObsA *obs5=NULL)
{
if(obs2==NULL)
{
if(getGlobalBounds().intersects(obs1->getGlobalBounds()))
{
relevel();
}
}
private:
float speed=0.00;
void obs()
{
if(speed > 0)
{
rotate(0.5*speed);
}
else
{
rotate(0.5*speed);
}
}
private:
float speed = 0.00;
void obs()
{
if(speed > 0)
{
rotate(0.5*speed);
}
else
{
rotate(0.5*speed);
}
}
private:
float speed = 0.00;
Is there something wrong with the code, how to fix the problem, thank you!
The intersects function just check if two rectangles intersect. If you want pixel perfect collision detection in SFML you have to write that yourself.
Basically, start with intersects, if it is true, then get the intersecting rectangle and check if any pixels therein from both original rectangles contains overlaping relevant pixels.
You can use this function to perform better collision detection.Its a basic one but works well
bool circleTest(const sf::Sprite &first, const sf::Sprite &second)
{
sf::Vector2f firstRect(first.getTextureRect().width, first.getTextureRect().height);
firstRect.x *= first.getScale().x;
firstRect.y *= first.getScale().y;
sf::Vector2f secondRect(second.getTextureRect().width, second.getTextureRect().height);
secondRect.x *= second.getScale().x;
secondRect.y *= second.getScale().y;
float r1 = (firstRect.x + firstRect.y) / 4;
float r2 = (secondRect.x + secondRect.y) / 4;
float xd = first.getPosition().x - second.getPosition().x;
float yd = first.getPosition().y - second.getPosition().y;
return std::sqrt(xd * xd + yd * yd) <= r1 + r2;
}
Are you using a circle? If I remember correctly, the circle will have a rectangle hitbox. If that is the case, then you may have collision between the invisible rectangle corners.
If you're using a circle, Perhaps change class to a square rectangle and see if collision works correctly. Or try testing collision directly on an x or y axis with your circles; i.e. having them moving in a straight line towards each other only changing 1 axis. (the edge of the circle will be the same as the edge of the rectangle at the left, right, top, and bottom sections).
If you're needing a better collision for circles, there may be one already built in SFML. But I don't think it would be too much to write your own logic using the radius of your two circles, the center of your two objects, and the angle hypotenuse between the centers.
edit based on Merlyn Morgan-Graham's comment.

How to draw a segment of a circle in Cocos2d-x?

Context
I try to draw pie chart for statistic in my game. I'm using Cocos2d-x ver.3.8.1. Size of the game is important, so I won't to use third-party frameworks to create pie charts.
Problem
I could not find any suitable method in Cocos2d-x for drawing part of the circle.
I tried to do
I tried to find a solution to this problem in Internet, but without success.
As is known, sector of a circle = triangle + segment. So, I tried to use the method drawSegment() from DrawNode also.
Although it has parameter radius ("The segment radius" written in API reference), radius affects only the thickness of the line.
drawSegment() method draw a simple line, the thickness of which is set by a method call.
Question
Please prompt me, how can I draw a segment or a sector of a circle in Cocos2d-x?
Any advice will be appreciated, thanks.
I think the one of the ways to draw a sector of a circle in Cocos2d-X is the way to use drawPolygon on DrawNode. I wrote little sample.
void drawSector(cocos2d::DrawNode* node, cocos2d::Vec2 origin, float radius, float angle_degree,
cocos2d::Color4F fillColor, float borderWidth, cocos2d::Color4F bordercolor,
unsigned int num_of_points = 100)
{
if (!node)
{
return;
}
const cocos2d::Vec2 start = origin + cocos2d::Vec2{radius, 0};
const auto angle_step = 2 * M_PI * angle_degree / 360.f / num_of_points;
std::vector<cocos2d::Point> circle;
circle.emplace_back(origin);
for (int i = 0; i <= num_of_points; i++)
{
auto rads = angle_step * i;
auto x = origin.x + radius * cosf(rads);
auto y = origin.y + radius * sinf(rads);
circle.emplace_back(x, y);
}
node->drawPolygon(circle.data(), circle.size(), fillColor, borderWidth, bordercolor);
}
This is the function to calculate the position of edge point of circle and draw polygon. If you want to use it, you need to call like following,
auto canvas = DrawNode::create();
drawSector(canvas, cocos2d::Vec2(400, 400), 100, 60, cocos2d::Color4F::GREEN, 2, cocos2d::Color4F::BLUE, 100);
this->addChild(triangle);
The result would be like this. I think the code will help your problem.

Cocos2d-x smooth drawing

i'm developing freehand drawing game, and the drawing isn't very smooth , i used this code:
void Canvas::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event) {
Canvas::drawEvenlySpacedSprites(touch->getLocation(),touch->getPreviousLocation());
}
void Canvas::drawEvenlySpacedSprites(Vec2 start, Vec2 end) {
// begin drawing to the render texture
_target->begin();
float distance = start.getDistance(end);
if (distance > 1) {
int d = (int)distance;
for (int i = 0; i < d; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
Sprite * sprite = Sprite::create("brush3.png");
sprite->setColor(Color3B::BLUE);
sprite->setPosition(Vec2(start.x + (difx * delta), start.y + (dify * delta)));
sprite->visit();
}
}
// finish drawing and return context back to the screen
_target->end();
}
I'm working with cocos2d-x V3.3
Please help me to get smooth drawing
Thanks
As Nadarian says, you should not create a new Sprite each time you draw it.
Even though you cannot reuse same sprite, you can create sprite from same SpriteFrame.
First, you need to store SpriteFrame on some variable. Or, you can use SpriteFrameCache if you want.
I will show the code which will use SpriteFrame.
// create sprite
auto sprite = cocos2d::Sprite::create("brush3.png");
// get spriteframe from sprite
auto spriteframe = sprite->getSpriteFrame();
and on Canvas::drawEvenlySpacedSprites, create Sprite from the stored spriteframe.
Here is the example, change the this code
Sprite * sprite = Sprite::create("brush3.png");
to this code,
Sprite * sprite = Sprite::createWithSpriteFrame(spriteframe);
I've found this implementation, I hope it will helps you: http://build-failed.blogspot.it/2012/08/freehand-drawing-with-cocos2d-x-and.html