i'm (trying) to develop a simple game using cocos2d-x v3 (see here).
The aim is to move the rainbow-ish player from the bottom green area to the top one.
The joystick is the white thumb on the left: it can be moved with the finger to change the player's direction.
So far, i'm using the built-in collision detector in cocos2d-x v3 (through the class PhysicalBody). The red borders in the figure represent the shape of the player are the borders of the arena it can freely move in.
When the user moves the joystick's thumb, its direction is used to set the player's velocity. However, being in physical world, setting the velocity is not the best choice because it breaks the physical laws, i.e. only a force can be applied to a body. Therefore, from the desired speed, I compute the impulse to be applied to achieve it:
Vec2 currentVel = _player->getPhysicsBody()->getVelocity();
Vec2 desiredVel = 80*_joystick->getDirection(); // a normalized Vec2
Vec2 deltaVel = desiredVel - currentVel;
Vec2 impulse = _player->getPhysicsBody()->getMass() * deltaVel;
_player->getPhysicsBody()->applyImpulse(impulse);
The problem is that the player can cross the edges as show here.
This happens when the player is in contact with the arena's edge and the impulse is applied.
I set the player's body as:
auto playerBody = PhysicsBody::createBox(_player->getContentSize(), PhysicsMaterial(100.0f, 0.0f, 0.0f));
playerBody->setDynamic(true);
playerBody->setRotationEnable(false);
playerBody->setGravityEnable(false);
_player->setPhysicsBody(playerBody);
where the three parameters of PhysicsMaterial are density, restitution and friction. In the same way, the map's body is:
auto mapBody = PhysicsBody::createEdgeChain(polygonPoints.data(), polygonPoints.size(), PhysicsMaterial(100.0f, 0.0f, 0.0f));
mapBody->setDynamic(false);
mapBody->setGravityEnable(false);
_tileMap->setPhysicsBody(mapBody);
where polygonPoints is a vector of Vec2's defining the shape. The player is a Sprite and the map is a TMXTiledMap.
I tried to change the values of density, friction and restitution without success.
Have you ever experienced the same problem?
Thanks!
It seems you are using the cocos2d-x implementation for Physics. So, i have less idea about this.
But Generally, this happens when update physics world update rate cycle is low, either by setting or low frame rate.
check, UpdateRate of your world.
From Doc:
/** Set the speed of physics world, speed is the rate at which the simulation executes. default value is 1.0 */
inline void setSpeed(float speed) { if(speed >= 0.0f) { _speed = speed; } }
/** get the speed of physics world */
inline float getSpeed() { return _speed; }
/**
* set the update rate of physics world, update rate is the value of EngineUpdateTimes/PhysicsWorldUpdateTimes.
* set it higher can improve performance, set it lower can improve accuracy of physics world simulation.
* default value is 1.0
*/
inline void setUpdateRate(int rate) { if(rate > 0) { _updateRate = rate; } }
/** get the update rate */
inline int getUpdateRate() { return _updateRate; }
Related
The past few days I've been trying to figure out a display bug I don't understand. I've been working on a simple 2d platformer with box2d and orthogonal Tiled maps. So far so good, the physics work and using the b2d debug renderer I can assert proper player fixture and camera movement through the level.
Now next step I've tried to load textures to display sprites instead of debug shapes. This is where I stumble. I can load animations for my player body/fixture, but when I use the setCenter() method to center the texture on the fixture it is always out of center.
I've tried approaches via halving texture witdths and heights hoping to center the texture on the player fixture but I get the exact same off position rendering. I've played aorund with world/camera/screen unit coordinates but the misalignement persists.
I'm creating the player in my Player class with the following code.
First I define the player in box2d:
//define player's physical behaviour
public void definePlayer() {
//definitions to later use in a body
BodyDef bdef = new BodyDef();
bdef.position.set(120 / Constants.PPM, 60 / Constants.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
b2body = world.createBody(bdef);
//Define needed components of the player's main fixture
FixtureDef fdef = new FixtureDef();
PolygonShape shape = new PolygonShape();
shape.setAsBox(8 / Constants.PPM, 16 / Constants.PPM); //size of the player hitbox
//set the player's category bit
fdef.filter.categoryBits = Constants.PLAYER_BIT;
//set which category bits the player should collide with. If not mentioned here, no collision occurrs
fdef.filter.maskBits = Constants.GROUND_BIT |
Constants.GEM_BIT |
Constants.BRICK_BIT |
Constants.OBJECT_BIT |
Constants.ENEMY_BIT |
Constants.TREASURE_CHEST_BIT |
Constants.ENEMY_HEAD_BIT |
Constants.ITEM_BIT;
fdef.shape = shape;
b2body.createFixture(fdef).setUserData(this);
}
Then I call the texture Region to be drawn in the Player class constructor:
//define in box2d
definePlayer();
//set initial values for the player's location, width and height, initial animation.
setBounds(0, 0, 64 / Constants.PPM, 64 / Constants.PPM);
setRegion(playerStand.getKeyFrame(stateTimer, true));
And finally, I update() my player:
public void update(float delta) {
//center position of the sprite on its body
// setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2);
setCenter(b2body.getPosition().x, b2body.getPosition().y);
setRegion(getFrame(delta));
//set all the boolean flags during update cycles approprietly. DO NOT manipulate b2bodies
//while the simulation happens! therefore, only set flags there, and call the appropriate
//methods outside the simulation step during update
checkForPitfall();
checkIfAttacking();
}
And my result is
this, facing right
and this, facing left
Update:
I've been trying to just run
setCenter(b2body.getPosition().x, b2body.getPosition().y);
as suggested, and I got the following result:
facing right and facing left.
The sprite texture flip code is as follows:
if((b2body.getLinearVelocity().x < 0 || !runningRight) && !region.isFlipX()) {
region.flip(true, false);
runningRight = false;
} else if ((b2body.getLinearVelocity().x > 0 || runningRight) && region.isFlipX()) {
region.flip(true, false);
runningRight = true;
}
I'm testing if either the boolean flag for facing right is set or the x-axis velocity of my player b2body has a positive/negative value and if my texture region is already flipped or not and then use libGDX's flip() accordingly. I should not be messing with fixture coords anywhere here, hence my confusion.
The coordinates of box2d fixtures are offsets from the position, the position isn't necessarily the center (although it could be depending on your shape definition offsets). So in your case i think the position is actually the lower left point of the box2d polygon shape.
In which case you don't need to adjust for width and height because sprites are also drawn from bottom left position. So all you need is ;
setPosition(b2body.getPosition().x , b2body.getPosition().y );
I'm guessing you flip the box2d body when the player looks left the position of the shape is now bottom right so the sprite offset of width/2 and height/2 is from the bottom right instead. So specifically when you are looking left you need an offset of
setPosition(b2body.getPosition().x - getWidth() , b2body.getPosition().y );
I think looking right will be fixed from this, but i don't know for sure how you handle looking left in terms of what you do to the body, but something is done because the offset changes entirely as shown in your capture. If you aren't doing some flipping you could add how you handle looking right to the question.
EDIT
It seems the answer was that the sprite wasn't centered in the sprite sheet and this additional space around the sprite caused the visual impression of being in the wrong place (see comments).
My question is whether I should place sRenderer.begin(ShapeType.Filled); and sRenderer.end(); outside of Shape? so they're not called for every Shape.draw()
Or is the approach below ok in terms of performance?
Snippet from one of my Shapes ..
private Body body; // initialized elsewhere
private float width = 1.0f;
private float height = 1.0f;
public void draw(ShapeRenderer sRenderer) {
sRenderer.begin(ShapeType.Filled);
sRenderer.setColor(1.0f, 0.0f, 0.0f, 1.0f);
sRenderer.identity();
sRenderer.translate(getBody().getPosition().x, getBody().getPosition().y, 0);
sRenderer.rotate(0.0f, 0.0f, 1.0f, (float) Math.toDegrees(getBody().getAngle()));
sRenderer.rect(-getWidth(), -getHeight(), getWidth() * 2, getHeight() * 2);
sRenderer.end();
}
In my Level class, theres a draw() method called every frame e.g
for (Body body : bodies) {
if (body.getUserData() instanceof Shape){
((Shape) body.getUserData()).draw(getShapeRenderer());
}
}
Each time you call begin(),identity(), translate(), rotate(), or etc., it will trigger a new flush to the GPU the next time you draw something with the shape renderer. So in your case, moving begin() and end() out of the loop won't have a very significant impact.
If this is a bottle neck, you could try calculating your rectangle corners independently from the shape renderer and submitting four lines to the shape renderer, using world space coordinates instead of local coordinates like you're doing now. Then move begin() and end() out of the loop like you suggested and benchmark the difference. I'm not sure which would be faster in your case. This would reduce draw calls but you'd be translating more points on the CPU as well.
I'm working on a new game written with LibGdx Engine and Java.
I've got a problem with some of the physics in this game.
I want to shoot the arrow in a ballistic trajectory (angry bird style)
and can't find the equation to do so .
I am using these velocity equations:
float velx = (float) (Math.cos(rotation) * spd);
float vely = (float) (Math.sin(rotation) * spd);
I add this to the current position and the arrow shoots in one direction - straight.
I thought maybe changing the rotation would help me achieve what I want (a ballistic path).
It does help, but I want to have the trajectory as well.
I saw this
ProjectileEquation class that someone already posted but didn't know how to work with it:
public class ProjectileEquation
{
public float gravity;
public Vector2 startVelocity = new Vector2();
public Vector2 startPoint = new Vector2();
public Vector2 gravityVec = new Vector2(0,-10f);
public float getX(float n) {
return startVelocity.x * (n ) + startPoint.x;
}
public float getY(float n) {
float t = n;
return 0.5f * gravity * t * t + startVelocity.y * t + startPoint.y;
}
}
I'm looking for some help to help me use this class for ballistic trajectories.
This is how I tried using it:
for(int i =0;i<30;i++)
{
Texture f = ResData.Square_1;
ProjectileEquation e= new ProjectileEquation();
e.gravity = 1;
e.startPoint = new Vector2(bow.getX(),bow.getY());//new Vector2(-bow.getX(),-bow.getY()); //My bow is opposite so it suppose to work fine
e.startVelocity = getVelocityOf(bow.getRotation());
Vector3 touchpos = new Vector3();
s.draw(f,e.getX(i) ,e.getX(i),5,5);
}
The ProjectileEquation class you post looks like it'll calculate the X and Y position given a time delta, so the float you pass in should be the time delta since you started the arrow moving (in seconds).
That code will not give you the angle of the arrow though. To find that, I would suggest you keep hold of the previous X and Y, then you can use Math.atan2() to calculate the angle based on the previous XY and the current XY. Google atan2 for a load of info on how to use it.
The very best way to do this however would be to use Box2d and model the scene correctly. Then you wouldn't have to get involved in the maths at all. I read somewhere that that's what Angry Birds uses, and is an excellent choice for modelling these sorts of physics games.
I hope your game goes well.
I'm trying to make a game that will take what users draw on their screens and create a sprite with a physics body. I looked around and I saw a tutorial that demonstrated this. Unfortunately the tutorial was made in Cocos2d-x v2.
http://build-failed.blogspot.com/2012/08/freehand-drawing-with-cocos2d-x-and.html
That is the tutorial that I am referring to.
http://www.cocos2d-x.org/wiki/Render_To_Texture
I tried to use that cocos2d-x tutorial to help me, but it has been labeled outdated (I tried it anyway and it didn't work). Is it still possible to allow the user to draw through this method? Or do I need to find another method to allow the user to draw the sprites? Any suggestions would be helpful. Thanks.
void GameScene::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event)
{
Point start = touch->getLocation();
start = Director::getInstance()->convertToGL(start);
Point end = touch->getPreviousLocation();
end = Director::getInstance()->convertToGL(end);
target->begin();
float distance = start.getDistance(end);
for (int i = 0; i < distance; i++)
{
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float) i / distance;
brush->setPosition(Point(start.x + (difx * delta), start.y + (dify * delta)));
brush->visit();
}
target->end();
}
void GameScene::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event)
{
myObjectSprite = Sprite::createWithTexture(target->getSprite()->getTexture());
myObjectSprite->setPosition(Point(visibleSize.width / 2, visibleSize.height / 2));
this->addChild(myObjectSprite);
}
This is what I gathered from those links. I can draw but there are a few problems.
The first attempt to draw always reflects horizontally over the center of the screen.
After a few drawings the fps begins to drop, and the app begins to take up a lot of memory and CPU usage.
The for loop in the onTouchMoved method is used because the onTouchEnded method isn't called fast enough, so there is often a large gap between each point that the onTouchMethod obtains. The loop is used to draw the brush sprites in a line between each point to prevent the gaps. However, for some reason it is not drawing in between the gaps.
Additionally, in the first link the person uses b2PolygonShape and b2FixtureDef from box2d. What are the new names of the classes in cocos2d-x v3?
My problem is that no matter the tuning I don't achieve a fast moving ball. It floats through the air rather than shoots through it.
In the following method I'm creating a ball with the radius of 5 units. Setting the radius to 0.01 yields no difference in the result.
After the creation I apply a LinearImpulse of ridiculous proportions yet, the ball only floats.
(Also, I'm using the DebugRender. Isn't that supposed to take care of the unit conversions?)
Ball class
public Body createBody(float x, float y, float w, float h) {
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(x,y);
Body body = this.getWorld().createBody(bodyDef);
CircleShape shape = new CircleShape();
shape.setRadius(5);
shape.setPosition(new Vector2(0, 0)); // relative to body position
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 3f;
fixtureDef.friction = 0.1f;
fixtureDef.restitution = 0.8f;
body.createFixture(fixtureDef);
shape.dispose();
// note the massive impulse vector
body.applyLinearImpulse(new Vector2(10000, 100000), body.getWorldCenter(), true);
return body;
}
render method in Game class
#Override
public void render(){
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
debugRenderer.render(world, camera.combined);
// time step, velocityIterations, positionIterations
world.step(1/40.0f, 6, 2);
// scheduler takes care of creating game objects on the fly
}
Okay, together with my answer to this question Is there an upper limit on velocity when using box2d? I'll try to explain your problem.
Box2D has a hard limit on velocity, which is 2meters per time step. That means the more timesteps you do per second, the faster your Bodies will be able to move. But since you actually want to have a fixed amount of timesteps per second, going with more timesteps is usually not an option.
What you should do instead is introducing a meter to pixel ratio which could be something in between 1:16 to 1:128. That means 1 meter in box2d should be translated to 16 or 128 pixels in screen units. That will increase the overall precision, because Box2D is more precise in smaller ranges. If you have a ratio of 1:1, it would mean that a standard screen these days would span a range of 1980m x 1080m, which is definetely too much. You can also see that with 60fps = 60 timesteps = 60*2m max distance per second, that would be just 120pixels per second at max speed. And this is what you currently experience as the floating body.
If you would translate 1m to 16px for example, your max speed would increase to 120px*16 = 7200px per second. That would already be sufficient in your case I guess.
Furthermore you should also set body.bullet to true to increase the collision precision for very fast moving bodies.
One of the reason is World scale, you may probably have a very big size world scale it done if it is a big.
It suggested that for better accuracy use set bullet to true however it takes more calculation
body.bullet = true;