I have a problem:
I am programming a tiled map game with Box2D but the proble is, that if I press for example D to go forward with my character, a vector 2 makes me going faster and faster so I did this:
if (Gdx.input.isKeyJustPressed(Input.Keys.W) && player.b2body.getLinearVelocity().y == 0)
player.b2body.applyLinearImpulse(new Vector2(0, 4f), player.b2body.getWorldCenter(), true);
if (Gdx.input.isKeyPressed(Input.Keys.D))
player.b2body.applyLinearImpulse(new Vector2(0.1f, 0), player.b2body.getWorldCenter(), true);
if (Gdx.input.isKeyPressed(Input.Keys.A))
player.b2body.applyLinearImpulse(new Vector2(-0.1f, 0), player.b2body.getWorldCenter(), true);
if (player.b2body.getLinearVelocity().x > 2) {
player.b2body.setLinearVelocity(2, player.b2body.getLinearVelocity().y);
}
else if (player.b2body.getLinearVelocity().x < -2) {
player.b2body.setLinearVelocity(-2, player.b2body.getLinearVelocity().y);
}
So the player has a maximum speed of two.
But when he hits the ground he is slower for around a half second because he gets faste in the air. How ca I fix that?
And my second questin is:
When I jump and press jump again right after I hit the goround the character doesnt jump!
Why and how can I fix that?
Hope you can help me and thanks in advance!
But when he hits the ground he is slower for around a half second
because he gets faste in the air. How ca I fix that?
I think that is because of the friction, try to set the friction to zero in the BodyDef when you create the body
When I jump and press jump again right after I hit the ground the
character doesn't jump! Why and how can I fix that?
i propose to you to do this :
if (Gdx.input.isKeyJustPressed(Input.Keys.W) && isPlayerOnGround)
player.b2body.applyLinearImpulse(new Vector2(0, 4f), player.b2body.getPosition(), true);
using world contact we detect if the player is on the ground
world.setContactListener(new () {
#Override
public void endContact(Contact c) {
Fixture fa = c.getFixtureA();
Fixture fb = c.getFixtureB();
/* end Contact
* <<Player>> <---> <<Gound>>
*/
if(fa.getUserData() !=null && fb.getUserData() !=null &&
((fa.getUserData().equals(playerUserData) && fb.getUserData().equals(groundUserData)) ||
fb.getUserData().equals(playerUserData) && fa.getUserData().equals(groundUserData)))
{
isPlayerOnGround = false;
}
}
#Override
public void beginContact(Contact c) {
Fixture fa = c.getFixtureA();
Fixture fb = c.getFixtureB();
/* Begin Contact
* * <<Player>> <---> <<Gound>>
*/
if(fa.getUserData() !=null && fb.getUserData() !=null &&
((fa.getUserData().equals(playerUserData) && fb.getUserData().equals(groundUserData)) ||
fb.getUserData().equals(playerUserData) && fa.getUserData().equals(groundUserData)))
{
isPlayerOnGround = true;
}
}
});
hope that was clear and helpful :=)
this is how you can add userData to a body
... // Define your bodyDef
Body body = world.createBody(bodyDef);
... // Define your fixtureDef
Fixture fixture = body.createFixture(fixtureDef);
String userData ="MyBody";
fixture.setUserData(userData);
when you put the fixture definition to your body you can get the fixture
just put the userdata (unique string ex : "mybody") to the fixture
PS: You can put the same userData to many bodies (a group of body) if you want.
Related
Really a basic question, but I am moving a sprite A with a physics body over another kind of sprites B having another physics body. I expect the collision callback oncontact to be called for these bodies. They have their respective category bitmasks set using setCategoryBitmask() and also respectively have each other's categories set using setContactTestBitmask().
The collision works as long as I do not move sprite A. I assume the problem is that I move sprite A using cocos2d actions, and I need to do something else. But using cocos2d actions for scripting things like these look so much simpler to me than anything else I can think of.
Move sprite A using physics calls. (looks like a lot of work, and it looks like it's hard to achieve exact scripting perfection)
Do my own collision detection in update() instead. (looks like a bunch of work too, especially if the sprites are rotated etc)
Is there any other shortcut? Or did I miss something else?
I ended up doing "Do my own collision detection in update() instead". It wasn't too problematic.
Something like this in update()...
const Rect RECT_A = spriteA->getBoundingBox();
for (auto spriteB : someparent->getChildren()) {
const Rect RECT_B = spriteB->getBoundingBox();
if (RECT_A.intersectsRect(RECT_B)) {
// Hit
}
}
Contaction detection should be fine while collion doesn't work.
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::createWithPhysics();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
scene->getPhysicsWorld()->setGravity(Vec2::ZERO);
// return the scene
return scene;
}
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
auto createSprite = [this](const Vec2& pos) {
auto sprite = Sprite::create();
auto bodyA = PhysicsBody::createCircle(20);
sprite->setPhysicsBody(bodyA);
sprite->setPosition(pos);
bodyA->setCollisionBitmask(0xff);
bodyA->setCategoryBitmask(0xff);
bodyA->setContactTestBitmask(0xff);
return sprite;
};
auto spriteA = createSprite(Vec2(300, 300));
auto moveBy = MoveBy::create(1.f, Vec2(200, 200));
spriteA->runAction(moveBy);
auto spriteB = createSprite(Vec2(350, 350));
addChild(spriteA);
addChild(spriteB);
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = [=](PhysicsContact& contact) {
auto a = contact.getShapeA()->getBody()->getNode();
auto b = contact.getShapeB()->getBody()->getNode();
assert((a == spriteA && b == spriteB) || (a == spriteB && b == spriteA));
log("It is working");
return false;
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(contactListener, this);
return true;
}
My game has a update method which handles the fire ability of my chracter.
my problem is the logic of my game, the bullet should fire from the position of my chracter, upon game start(not moving the character) fire bullet come from the position of the character, but when i move my character the start position of bullet is not same with the position of character.
The direction of the bullet depends on the direction of the player.
private void update() {
Vector2 direction = new Vector2(0, 0);
if (Gdx.input.isKeyPressed(Keys.D)) {
direction.x = 1f ;
}
if (Gdx.input.isKeyPressed(Keys.A)) {
direction.x = -1f ;
}
if (Gdx.input.isKeyPressed(Keys.W)) {
direction.y = 1f ;
}
if (Gdx.input.isKeyPressed(Keys.S)) {
direction.y = -1f;
}
if (direction.x != 0 || direction.y != 0) {
playerDirection.set(direction);
System.out.println("player x: " +playerDirection.x + "\t" +"player y:"+playerDirection.y);
}
if (Gdx.input.isKeyPressed(Keys.F)) {
bulletPos = new Vector2(startPos);
bulletDirection.set(playerDirection);
}
if (bulletPos != null) {
bulletPos.x += direction.x;
bulletPos.y +=direction.y;
if (bulletPos.x < 0 || bulletPos.x > mapPixelWidth
|| bulletPos.y < 0 || bulletPos.y > mapPixelHeight) {
bulletPos = null;
}
}
}
can anyone knows the logic error, or anyone there can provide simple logic of shooting that fire on a direction?
From what i see, you're always firing button from players start position (startPos), and then, when the bullet is moving, you update its position according to player direction (direction.x, .y)? But when player changes direction, bullet is starting to move other way as well. Thats wrong.
After its created, bullet should be always moved independent from your player. So when you create it, it has to have own start position (taken from current player position), and own direction (based on current player position). Your player movement cannot change bullet position anymore.
Something like:
if (Gdx.input.isKeyPressed(Keys.F)) {
bulletPos.set(currentPlayerPosition);
bulletDirection.set(currentPlayerDirection);
}
//and then in update()
bulletPos.x+=bulletDirection.x;
bulletPos.y+=bulletDirection.y;
//until it goes out of screen, or passes given distance,
//or new bullet is created, or whatever condition you like
this is how I remove my body from the world: _world->DestroyBody(spriteBody);
In my game, I constantly create Cocos2d Sprite and Box2D Body and I also constantly delete them.
However, when I try to LOG("%d", _world->GetBodyCount()); the number is increasing and never decrease.
I think _world->DestroyBody(spriteBody); might not completely delete my Box2D body.
This is my delete method.
void GameScene::deleteSprite(Sprite *sprite){
b2Body *spriteBody = NULL;
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
Sprite *curSprite = (Sprite *)b->GetUserData();
if (sprite == curSprite) {
log("3");
spriteBody = b;
break;
}
}
}
if (spriteBody != NULL) {
//spriteBody->SetUserData(NULL);
//b2Fixture* f = spriteBody->GetFixtureList();
//spriteBody->DestroyFixture(f);
spriteBody->GetWorld()->DestroyBody(spriteBody);
}
sprite->removeFromParentAndCleanup(true);
}
Create method - I took this code from GMTDev and Raywenderlich. Box2D for collision detection only
void GameScene::addBoxBodyForSprite( Sprite *sprite, int iNumVerts, b2Vec2 verts[] ){
if( _world==NULL )
return;
Point pos = sprite->getPosition();
Size size = sprite->getContentSize();
b2BodyDef spriteBodyDef;
spriteBodyDef.type = b2_dynamicBody;
spriteBodyDef.position.Set(pos.x/PTM_RATIO, pos.y/PTM_RATIO);
spriteBodyDef.userData = sprite;
b2Body *spriteBody = _world->CreateBody(&spriteBodyDef);
b2PolygonShape spriteShape;
if( iNumVerts!=0 )
{
spriteShape.Set(verts, iNumVerts);
b2FixtureDef spriteShapeDef;
spriteShapeDef.shape = &spriteShape;
spriteShapeDef.density = 10.0;
spriteShapeDef.isSensor = true;
spriteBody->CreateFixture(&spriteShapeDef);
}
else
{
// No Vertice supplied so just make a box round the sprite
b2BodyDef spriteBodyDef;
spriteBodyDef.type = b2_dynamicBody;
spriteBodyDef.position.Set( pos.x/PTM_RATIO, pos.y/PTM_RATIO );
spriteBodyDef.userData = sprite;
b2Body *spriteBody = _world->CreateBody( &spriteBodyDef );
b2PolygonShape spriteShape;
spriteShape.SetAsBox( size.width/PTM_RATIO/2, size.height/PTM_RATIO/2 );
b2FixtureDef spriteShapeDef;
spriteShapeDef.shape = &spriteShape;
spriteShapeDef.density = 10.0;
spriteShapeDef.isSensor = true; // isSensor true when you want to know when objects will collide without triggering a box2d collision response
spriteBody->CreateFixture( &spriteShapeDef );
}
}
Thanks !!
The problem is solved from the help of #UmeshSharma and #LearnCocos2D
in the create method that I got from GMTDev has some mistakes where the function _world->CreateBody is used twice (when iNumVerts == 0)
Therefore, when I delete my body and sprite. There is actually another body remaining for that sprite. And then it crash on the update where the Box2D body cannot find its Cocos2d sprite.
I am currently coding a game, and have encountered an annoying glitch. Occasionally, when you kill an enemy in game, it will drop extra currency, and another enemy onscreen will be removed from the enemylist. However, this second enemy, the one removed from the enemylist, will still be on screen, and will still shoot at the player. Below are code snippets from the collision formula, the enemies destruction sequence, and the bullet hitcheck sequence.
Collision formula:
public function testCollision(enemy:Entity):Boolean{
var eX:Number = enemy.collision.xPos
var eY:Number = enemy.collision.yPos
var eSL:Number = enemy.collision.SideLength/2
if(eX-xPos<(SideLength/2)+eSL && eY-yPos<(SideLength/2)+eSL && eX-xPos>-(SideLength/2)-eSL && eY-yPos>-(SideLength/2)-eSL){
return true
}else{
return false
}
}
Enemy destruction sequence:
if(deathVar){
view.transparency -= 1/20
if(view.transparency<0.1){
var cur = new PixelCurrency(2)
cur.collision.xPos = collision.xPos
cur.collision.yPos = collision.yPos
entityCreated.dispatch(cur)
destroy()
}
}
Bullet hitcheck:
for each (var enemy:Entity in Game.entities){
if(enemy.allies == Pixapocalypse.EnemyFaction){
if(collision.testCollision(enemy)){
if(enemy.life){
enemy.life.changeHealth(-2)
this.sound.playSound(new basicHitSound())
this.destroy()
break
}
else{
}
}
}
}
If you need any extra info, please tell me, I am greatly appreciative of your help.
What happens when Enemy.life < 0 ?
I'm current building a game in as3; the proplem I have right now is when I roll the virtual dice, the player(marker) moves accross the board but what I need to know is: is there a way to find the instance name of the object(box) that the player lands on?
And Sorry my english isn't good.
It depends a lot on how your board is laid out. One way is to put all of the objects your player can land on into an array, then check the player's x and y coordinates to see if they fall inside of each object's box.
For example:
var boardObjects:Array; // This would contain references to all the objects the
// player object might land on. Initialize it, then use boardObjects.add(object)
// on each one until they're all in the array.
// once the player has moved:
for(var i:int = 0; i < boardObjects.size; i++) {
var obj:* = boardObjects[i];
if (player.x >= obj.x && player.x <= obj.x + obj.width) {
if (player.y >= obj.y && player.y <= obj.y + obj.height) {
// If these if statements are all true, the Player's top-left corner
// is inside the object's bounding box. If this is a function,
// here is a good spot to put a return statement.
}
}
}
You may want to calculate it based on the middle of the player rather than their top-left corner, in which case just add half the player's width to their x position and half their height to their y position.
For performance (and avoiding unnecessary code), if it's tile based / dice why not do something like this
private function rollDice(){
var results:Array = [Math.ceil(Math.random() * 6), Math.ceil(Math.random() * 6)] //Accurately simulates two 6 sided dice
dice1.rollAnimation(results[0]);
dice2.rollAnimation(results[1]);
player.position += results[0] + results[1];
}
The board would be an array, and in Player you can use getters/setters to 'wrap' the board like this
private var _position:int = 0;
public function get position():int{
return _position;
}
public function set position(value:int){
_position = value;
while(_position > GameBoard.TILES){
_position -= GameBoard.TILES;
}
x = //Whatever you determine the positioning of the player..
}