I'm trying to get Box2D to work with SFML, although I'm having some issues like this:
For some reason ball just stops before hitting the platform. I tried varying the ball's position across x-axis and it falled out of the window,so I guess the drawing is off. Since SFML uses top-left origin for objects, but Box2D uses it's center I had to offset that in the draw functions for box and the ball:
void Ball::draw(sf::RenderWindow & window)
{
circleS.setPosition(sf::Vector2f(body->GetPosition().x - (circle.m_radius
/2),
body->GetPosition().y - (circle.m_radius 2)));
circleS.setRotation(body->GetAngle() * (180.0f / 3.14159f));
window.draw(circleS);
}
void Box::draw(sf::RenderWindow & window)
{
boxShape.setPosition(sf::Vector2f(body->GetPosition().x - (dimension.x / 2),
body->GetPosition().y - (dimension.y / 2)));
boxShape.setRotation(body->GetAngle() * (180.0f / 3.14159f));
window.draw(boxShape);
}
I don't understand why this isn't working,but if it's any help I can include more code. All help is appreciated.
Edit:
I replaced the ball with a box and it worked fine; then I changed up ball back and bottom box was changed to static ball and it just passed through,also here's a snippet of my main function:
RenderWindow window;
window.create(VideoMode(SCREENWIDTH, SCREENHEIGHT), "Pool game");
Event event;
//Setup for Box2D
b2Vec2 gravity(0.0f, 9.81f);
b2World world(gravity);
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 8;
int32 positionIterations = 3;
Box box(b2Vec2(100.0f, 400.0f), b2Vec2(100.0f, 20.0f), Color::Magenta,
world);
Ball ball(b2Vec2(50.0f, 50.0f), 9.0f, Color::Blue, world, true);
//game loop
while (window.isOpen()) {
//window closure
while (window.pollEvent(event)) {
if (event.type == Event::Closed) {
window.close();
}
}
world.Step(timeStep, velocityIterations, positionIterations);
//////// Drawing ////////
window.clear(Color(0,128,0));
//------------------------------------------
box.draw(window);
ball.draw(window);
//------------------------------------------
window.display();
//------------------------------------
}
Edit 2:
Here a header files for Ball and Box classes:
class Box {
//Constructors
public:
Box(b2Vec2 position, b2Vec2 dimension, sf::Color c, b2World &world, bool
isDynamic = false) : dimension(dimension) {
if (isDynamic) bodyDef.type = b2_dynamicBody; else bodyDef.type =
b2_staticBody;
bodyDef.position.Set(position.x , position.y );
body = world.CreateBody(&bodyDef);
dynamicBox.SetAsBox(dimension.x / 2, dimension.y / 2 );
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
boxShape.setPosition(sf::Vector2f(body->GetPosition().x - (this-
>dimension.x / 2), body->GetPosition().y - (this->dimension.y / 2)));
boxShape.setSize(sf::Vector2f(this->dimension.x, this->dimension.y));
boxShape.setFillColor(c);
}
~Box() {
body = nullptr;
}
//Functions
public:
void draw(sf::RenderWindow &window);
//Variables
public:
//Box2D
b2BodyDef bodyDef;
b2PolygonShape dynamicBox;
b2FixtureDef fixtureDef;
b2Body* body;
//SFML
sf::RectangleShape boxShape;
b2Vec2 dimension;
};
And also ball :
class Ball {
//Constructors
public:
Ball(b2Vec2 position,float radius, sf::Color c, b2World &world, bool
isDynamic = false) {
if (isDynamic) bodyDef.type = b2_dynamicBody; else bodyDef.type =
b2_staticBody;
bodyDef.position.Set(position.x , position.y );
body = world.CreateBody(&bodyDef);
circle.m_p.Set(position.x,position.y);
circle.m_radius = radius + outlineThickness;
fixtureDef.shape = &circle;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
circleS.setPosition(sf::Vector2f(body->GetPosition().x -
(circle.m_radius / 2),body->GetPosition().y - (circle.m_radius / 2)));
circleS.setRadius(radius);
circleS.setOutlineThickness(outlineThickness);
circleS.setOutlineColor(sf::Color::Black);
circleS.setFillColor(c);
}
~Ball() {
body = nullptr;
}
//Functions
public:
void draw(sf::RenderWindow &window);
//Variables
public:
//Box2D
b2BodyDef bodyDef;
b2CircleShape circle;
b2FixtureDef fixtureDef;
b2Body* body;
//SFML
sf::CircleShape circleS;
float outlineThickness = 1.0f;
};
Related
I am trying to detect a collision between a static body and a kinematic body using the b2ContactListener class but my code is not detecting a collision between my two fixtures. I placed an EdgeShape at the top of my kinematic body and made it a fixture to detect a collision with my static body fixture. I am using SFML to draw to the screen and detect keyboard events.
Here is my main function:
int main()
{
Vector2f resolution;
resolution.x = VideoMode::getDesktopMode().width;
resolution.y = VideoMode::getDesktopMode().height;
RenderWindow window(VideoMode(resolution.x, resolution.y),
"Box2D Test", Style::Fullscreen);
Vector2f staticRectSize(100.0f, 20.0f);
Vector2f kinematicRectSize(40.0f, 20.0f);
Event keyBoardEvent;
b2Vec2 gravity(0.0f, 0.0f);
b2World world(gravity);
// Creating static body and fixture
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(100.0f, 150.0f);
b2Body* groundBody = world.CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
groundBox.SetAsBox(50.0f, 10.0f);
groundBody->CreateFixture(&groundBox, 0.0f);
// Creating kinematic body and fixture
b2BodyDef bodyDef;
bodyDef.type = b2_kinematicBody;
bodyDef.position.Set(125.0f, 700.0f);
b2Body* body = world.CreateBody(&bodyDef);
b2PolygonShape kinematicBox;
kinematicBox.SetAsBox(20.0f, 10.0f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &kinematicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
// world.Step inputs
float32 timeStep = 1.0f / 60.0f;
int32 velocityIterations = 6;
int32 positionIterations = 2;
RectangleShape staticRect(staticRectSize);
RectangleShape dynamicRect(kinematicRectSize);
b2Vec2 staticPosition = groundBody->GetPosition();
staticRect.setPosition(staticPosition.x, staticPosition.y);
b2Vec2 dynamicPosition = body->GetPosition();
// Creating EdgeShape
b2EdgeShape top;
top.Set(b2Vec2(dynamicPosition.x, dynamicPosition.y), b2Vec2(dynamicPosition.x + 40.0, dynamicPosition.y));
fixtureDef.shape = ⊤
fixtureDef.isSensor = true;
body->CreateFixture(&fixtureDef)->SetUserData("top collision");
// Creating an instance of myContactListener class
myContactListener contactListener;
world.SetContactListener(&contactListener);
while (window.isOpen())
{
world.Step(timeStep, velocityIterations, positionIterations);
dynamicPosition = body->GetPosition();
dynamicRect.setPosition(dynamicPosition.x, dynamicPosition.y);
while (window.pollEvent(keyBoardEvent))
{
if (keyBoardEvent.type == Event::KeyPressed)
{
if (keyBoardEvent.key.code == Keyboard::W)
{
body->SetLinearVelocity(b2Vec2(0, -10));
}
...
window.clear();
window.draw(staticRect);
window.draw(dynamicRect);
window.draw(line);
window.display();
if (Keyboard::isKeyPressed(Keyboard::Escape))
{
break;
}
}
return 0;
}
And here is my MyContactListiner.cpp:
void myContactListener::BeginContact(b2Contact* contact)
{
b2Fixture *fa = contact->GetFixtureA();
b2Fixture *fb = contact->GetFixtureB();
if (fa == NULL || fb == NULL) { return; }
while (1)
{
printf("Collision occurred");
if (Keyboard::isKeyPressed(Keyboard::Escape))
{
break;
}
}
}
Box2D doesn't do collision handling for static bodies directly against other static or kinematic bodies. They're both theoretically of infinite mass for which laws of motion don't seem to make much sense anymore (at least not to me in terms of collisions).
Probably easiest to make the kinematic body a dynamic one instead.
You could alternatively surround one of the bodies with dynamic ones to get the contact listener to fire up. The dynamic bodies can't be made to drive the kinematic or static one it surrounds however in the substep phases without more effort than I think it'd be worth.
Hope this answer helps!
I'm trying to get physics working in SFML using Box2D, but for some reason I can't set the position of my sprite in my class.
I created a TestBox for my game, basically just a box that has physics, but it doesn't seem to be working. I am able to set the position of the box in my class constructor, but not in the update function I created.
So onto the code:
TestBox.h:
#pragma once
#include<Box2D/Box2D.h>
#include<SFML/Graphics.hpp>
#include<string>
#include<iostream>
class TestBox : public sf:: Drawable
{
public:
TestBox(b2World *world, std::string texturePath, float x, float y, float w, float h);
void update();
b2Body *getBody() { return body; }
b2Fixture *getFixture() { return fixture; }
private:
virtual void draw(sf::RenderTarget &target, sf::RenderStates states) const;
b2Body *body = nullptr;
b2Fixture *fixture = nullptr;
sf::Texture texture;
sf::Sprite boxSprite;
};
TestBox.cpp:
#include "TestBox.h"
TestBox::TestBox(b2World *world, std::string texturePath, float x, float y, float w, float h) {
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(x, y);
body = world->CreateBody(&bodyDef);
b2PolygonShape boxShape;
boxShape.SetAsBox(w / 2.0f, h / 2.0f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &boxShape;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
fixture = body->CreateFixture(&fixtureDef);
texture.loadFromFile(texturePath.c_str());
boxSprite.setTexture(texture);
boxSprite.setOrigin(16.f, 16.f);
boxSprite.setPosition(30.0f * body->GetPosition().x, 30.0f * body->GetPosition().y);
boxSprite.setRotation(body->GetAngle() * 180 / b2_pi);
}
void TestBox::update() {
std::cout << boxSprite.getPosition().x << ", " << boxSprite.getPosition().y << std::endl;
boxSprite.setPosition(30.0f * body->GetPosition().x, 30.0f * body->GetPosition().y);
boxSprite.setRotation(body->GetAngle() * 180 / b2_pi);
}
void TestBox::draw(sf::RenderTarget &target, sf::RenderStates states) const {
target.draw(boxSprite, states);
}
So, I've tried to narrow down my problem as much as possible, and rule as much as I could out. Here is what I found:
I am able to set the position in the constructor, this works fine.
I have been checking the position of the Box2D body, the position of that changes fine.
I have checked the math:
30.0f * body->GetPosition().x
30.0f * body->GetPosition().y
this is accurate and works fine.
The update function does run, it prints normally.
The result of printing the position is always the start position
Let me know if I left out any information you could need.
When I compile and run application, for example this source code (from tutorial, I had to fix this code, ex. big letters in names of methods):
#include <SFML\Graphics.hpp>
#include <Box2D\Box2D.h>
/** We need this to easily convert between pixel and real-world coordinates*/
static const float SCALE = 30.f;
/** Create the base for the boxes to land */
void CreateGround(b2World& World, float X, float Y);
/** Create the boxes */
void CreateBox(b2World& World, int MouseX, int MouseY);
int main()
{
/** Prepare the window */
sf::RenderWindow Window(sf::VideoMode(800, 600, 32), "Test");
Window.SetFramerateLimit(60);
/** Prepare the world */
b2Vec2 Gravity(0.f, 9.8f);
b2World World(Gravity);
CreateGround(World, 400.f, 500.f);
/** Prepare textures */
sf::Texture GroundTexture;
sf::Texture BoxTexture;
GroundTexture.LoadFromFile("ground.png");
BoxTexture.LoadFromFile("box.png");
while (Window.IsOpened())
{
if (sf::Mouse::IsButtonPressed(sf::Mouse::Left))
{
int MouseX = sf::Mouse::GetPosition(Window).x;
int MouseY = sf::Mouse::GetPosition(Window).y;
CreateBox(World, MouseX, MouseY);
}
World.Step(1/60.f, 8, 3);
Window.Clear(sf::Color::White);
int BodyCount = 0;
for (b2Body* BodyIterator = World.GetBodyList(); BodyIterator != 0; BodyIterator = BodyIterator->GetNext())
{
if (BodyIterator->GetType() == b2_dynamicBody)
{
sf::Sprite Sprite;
Sprite.SetTexture(BoxTexture);
Sprite.SetOrigin(16.f, 16.f);
Sprite.SetPosition(SCALE * BodyIterator->GetPosition().x, SCALE * BodyIterator->GetPosition().y);
Sprite.SetRotation(BodyIterator->GetAngle() * 180/b2_pi);
Window.Draw(Sprite);
++BodyCount;
}
else
{
sf::Sprite GroundSprite;
GroundSprite.SetTexture(GroundTexture);
GroundSprite.SetOrigin(400.f, 8.f);
GroundSprite.SetPosition(BodyIterator->GetPosition().x * SCALE, BodyIterator->GetPosition().y * SCALE);
GroundSprite.SetRotation(180/b2_pi * BodyIterator->GetAngle());
Window.Draw(GroundSprite);
}
}
Window.Display();
}
return 0;
}
void CreateBox(b2World& World, int MouseX, int MouseY)
{
b2BodyDef BodyDef;
BodyDef.position = b2Vec2(MouseX/SCALE, MouseY/SCALE);
BodyDef.type = b2_dynamicBody;
b2Body* Body = World.CreateBody(&BodyDef);
b2PolygonShape Shape;
Shape.SetAsBox((32.f/2)/SCALE, (32.f/2)/SCALE);
b2FixtureDef FixtureDef;
FixtureDef.density = 1.f;
FixtureDef.friction = 0.7f;
FixtureDef.shape = &Shape;
Body->CreateFixture(&FixtureDef);
}
void CreateGround(b2World& World, float X, float Y)
{
b2BodyDef BodyDef;
BodyDef.position = b2Vec2(X/SCALE, Y/SCALE);
BodyDef.type = b2_staticBody;
b2Body* Body = World.CreateBody(&BodyDef);
b2PolygonShape Shape;
Shape.SetAsBox((800.f/2)/SCALE, (16.f/2)/SCALE);
b2FixtureDef FixtureDef;
FixtureDef.density = 0.f;
FixtureDef.shape = &Shape;
Body->CreateFixture(&FixtureDef);
}
My application is "not responding" after few seconds. The window turns to grey but, everything works! Objects are moving and all interactions are working. What is the problem?
In the game I'm trying to make, I have a ball sprite which bounces thanks to box2d. Here's how my current code looks:
-(id)init
{
ball = [CCSprite spriteWithFile:#"ball.png"];
ball.position = ccp(150, winSize.height * 0.78);
[self addChild:ball];
ball.tag = 2;
b2BodyDef ballBodyDef;
ballBodyDef.type = b2_dynamicBody;
ballBodyDef.position.Set(150/PTM_RATIO, 450/PTM_RATIO);
ballBodyDef.userData = ball;
_body = _world->CreateBody(&ballBodyDef);
b2CircleShape circle;
circle.m_radius = 26.0/PTM_RATIO;
b2FixtureDef ballShapeDef;
ballShapeDef.shape = &circle;
ballShapeDef.density = 0.5f;
ballShapeDef.friction = 1.0f;
ballShapeDef.restitution = 1.0f;
_ballFixture = _body->CreateFixture(&ballShapeDef);
b2Vec2 force = b2Vec2(160, 375);
_body->ApplyLinearImpulse(force, ballBodyDef.position);}
- (void)update:(ccTime) dt {
if(_isPaused == FALSE)
{
_world->Step(dt, 10, 10);
for(b2Body *b = _world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *sprite = (CCSprite *)b->GetUserData();
if(sprite.tag == 2)
{
sprite.position = ccp(b->GetPosition().x * PTM_RATIO,
b->GetPosition().y * PTM_RATIO);
sprite.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
}
}
}}
Bouncing itself works fine, my problem is there are instances wherein the ball would bounce on a straight line so to speak, either vertically or horizontally continuously which I am trying to avoid. So my question is, how can I make my ball sprite bounce at an angle instead of a straight line so it wouldn't get stuck bouncing infinitely in the same direction?
You could apply a tiny force or gravity change to the body or the world, "randomly" or at equal intervals.
Can you help. Want to draw a polygon (beams at different angles) and apply box 2d body to it. Can you please let me know how to create a CCSprite with a polygon shape
Any examples would help
Cheers
Create Polygon body.
-(void) createDynamicPoly {
b2BodyDef bodyDefPoly;
bodyDefPoly.type = b2_dynamicBody;
bodyDefPoly.position.Set(3.0f, 10.0f);
b2Body *polyBody = world->CreateBody(&bodyDefPoly);
int count = 8;
b2Vec2 vertices[8];
vertices[0].Set(0.0f / PTM_RATIO,0.0f / PTM_RATIO);
vertices[1].Set(48.0f/PTM_RATIO,0.0f/PTM_RATIO);
vertices[2].Set(48.0f/PTM_RATIO,30.0f/PTM_RATIO);
vertices[3].Set(42.0f/PTM_RATIO,30.0f/PTM_RATIO);
vertices[4].Set(30.0f/PTM_RATIO,18.0f/PTM_RATIO);
vertices[5].Set(18.0f/PTM_RATIO,12.0f/PTM_RATIO);
vertices[6].Set(6.0f/PTM_RATIO,18.0f/PTM_RATIO);
vertices[7].Set(0.0f/PTM_RATIO,30.0f/PTM_RATIO);
b2PolygonShape polygon;
polygon.Set(vertices, count);
b2FixtureDef fixtureDefPoly;
fixtureDefPoly.shape = &polygon;
fixtureDefPoly.density = 1.0f;
fixtureDefPoly.friction = 0.3f;
polyBody->CreateFixture(&fixtureDefPoly);
}
Create your sprite
Attach your sprite to the Polygon body via Fixture and UserData
fixtureDefPoly.SetUserData() = spriteObject;
b2Fixture *fixture;
fixture = circleBody->CreateFixture(&fixtureDefPoly);
fixture->SetUserData(#"spriteObject");
Then Iterate the sprite to the body in your update method.
The easiest way is to open an image editor (such as paint for example or photoshop) and create the image you want. The use it in your program.
Also there is a helloWorld scene when creating an xcode application using cocos2d box2d template. It creates a set of squares with a texture.
CGPoint startPt = edge.start ;
CGPoint endpt = edge.end ;
//length of the stick body
float len = abs(ccpDistance(startPt, endpt))/PTM_RATIO;
//to calculate the angle and position of the body.
float dx = endpt.x-startPt.x;
float dy = endpt.y-startPt.y;
//position of the body
float xPos = startPt.x+dx/2.0f;
float yPos = startPt.y+dy/2.0f;
//width of the body.
float width = 1.0f/PTM_RATIO;
b2BodyDef bodyDef;
bodyDef.position.Set(xPos/PTM_RATIO, yPos/PTM_RATIO);
bodyDef.angle = atan(dy/dx);
NSLog([NSString stringWithFormat:#"Setting angle %f",bodyDef.angle]);
CCSprite *sp = [CCSprite spriteWithFile:#"material-wood.png" rect:CGRectMake(0, 0, 12, 12)];
//TODO: fix shape
[self addChild:sp z:1 ];
bodyDef.userData = sp;
bodyDef.type = b2_dynamicBody;
b2Body* body = world->CreateBody(&bodyDef);
b2PolygonShape shape;
b2Vec2 rectangle1_vertices[4];
rectangle1_vertices[0].Set(-len/2, -width/2);
rectangle1_vertices[1].Set(len/2, -width/2);
rectangle1_vertices[2].Set(len/2, width/2);
rectangle1_vertices[3].Set(-len/2, width/2);
shape.Set(rectangle1_vertices, 4);
b2FixtureDef fd;
fd.shape = &shape;
fd.density = 1.0f;
fd.friction = 0.300000f;
fd.restitution = 0.600000f;
body->CreateFixture(&fd);