I want to control the box by 2 fingers like below:
I have basic MouseJoint implementation:
public class MyMouseJoint{
OrthographicCamera cam;
World world;
Body groundBody ;
public MouseJoint mouseJoint = null;
Body hitBody = null;
Vector2 target = new Vector2();
Vector3 testPoint = new Vector3();
QueryCallback callback = new QueryCallback() {
#Override
public boolean reportFixture (Fixture fixture) {
// if the hit fixture's body is the ground body we ignore it
if (fixture.getBody() == groundBody) return true;
// if the hit point is inside the fixture of the body
// we report it
if (fixture.testPoint(testPoint.x, testPoint.y)) {
hitBody = fixture.getBody();
return false;
} else
return true;
}
};
public MyMouseJoint(OrthographicCamera cam, World world, Body groundBody){
this.cam=cam;
this.world=world;
this.groundBody = groundBody;
}
//USE THIS FUNCTION IN touchDown
public void createMouseJoint(float x, float y){
// translate the mouse coordinates to world coordinates
testPoint.set(x, y, 0);
cam.unproject(testPoint);
// ask the world which bodies are within the given
// bounding box around the mouse pointer
hitBody = null;
world.QueryAABB(callback, testPoint.x - 0.1f, testPoint.y - 0.1f, testPoint.x + 0.1f, testPoint.y + 0.1f);
if (hitBody != null) {
MouseJointDef def = new MouseJointDef();
def.bodyA = groundBody;
def.bodyB = hitBody;
def.collideConnected = true;
def.target.set(testPoint.x, testPoint.y);
def.maxForce = 10000.0f * hitBody.getMass();
def.frequencyHz=100;
def.dampingRatio=0;
mouseJoint = (MouseJoint)world.createJoint(def);
hitBody.setAwake(true);
}
}
//USE THIS FUNCTION IN touchDragged
public void dragMouseJoint(float x, float y){
if (mouseJoint != null) {
cam.unproject(testPoint.set(x, y, 0));
mouseJoint.setTarget(target.set(testPoint.x, testPoint.y));
}
}
//USE THIS FUNCTION IN touchUp
public void releaseMouseJoint(){
if (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
}
}
}
How can modify this class for using 2 fingers?
What you can do is create an array of Body and initialize hitbody[] with the pointer of your touch. you can change the above code to following code.
In the following function the varable pointer is the pointer if your current touch
public class MyMouseJoint{
OrthographicCamera cam;
World world;
Body groundBody ;
public MouseJoint mouseJoint[] = new MouseJoint[2];
Body hitBody[] = new Body[2];
Vector2 target = new Vector2();
Vector3 testPoint = new Vector3();
Body tempBody;
QueryCallback callback = new QueryCallback() {
#Override
public boolean reportFixture (Fixture fixture) {
// if the hit fixture's body is the ground body we ignore it
if (fixture.getBody() == groundBody) return true;
// if the hit point is inside the fixture of the body
// we report it
if (fixture.testPoint(testPoint.x, testPoint.y)) {
tempBody = fixture.getBody();
return false;
} else
return true;
}
};
public MyMouseJoint(OrthographicCamera cam, World world, Body groundBody){
this.cam=cam;
this.world=world;
this.groundBody = groundBody;
}
//USE THIS FUNCTION IN touchDown
public void createMouseJoint(float x, float y,int pointer){
// translate the mouse coordinates to world coordinates
testPoint.set(x, y, 0);
cam.unproject(testPoint);
// ask the world which bodies are within the given
// bounding box around the mouse pointer
hitBody[pointer] = null;
world.QueryAABB(callback, testPoint.x - 0.1f, testPoint.y - 0.1f, testPoint.x + 0.1f, testPoint.y + 0.1f);
hitBody[pointer] = tempBody;
if (hitBody[pointer] != null) {
MouseJointDef def = new MouseJointDef();
def.bodyA = groundBody;
def.bodyB = hitBody[pointer];
def.collideConnected = true;
def.target.set(testPoint.x, testPoint.y);
def.maxForce = 10000.0f * hitBody[pointer].getMass();
def.frequencyHz=100;
def.dampingRatio=0;
mouseJoint[pointer] = (MouseJoint)world.createJoint(def);
hitBody[pointer].setAwake(true);
}
}
//USE THIS FUNCTION IN touchDragged
public void dragMouseJoint(float x, float y,int pointer){
if (mouseJoint[pointer] != null) {
cam.unproject(testPoint.set(x, y, 0));
mouseJoint[pointer].setTarget(target.set(testPoint.x, testPoint.y));
}
}
//USE THIS FUNCTION IN touchUp
public void releaseMouseJoint(int pointer){
if (mouseJoint[pointer] != null) {
world.destroyJoint(mouseJoint[pointer]);
mouseJoint[pointer] = null;
}
}
}
Related
I am new to OpenGL and ARCore and I am using GoogleARCore Sample as a base to create my application. I am using OpenGL-ES-2.0 version. I able to do Pinch zoom (2 fingers) using android.view.ScaleGestureDetector.SimpleOnScaleGestureListener. By using this library class of Rotation Rotation Gesture I am able to get my rotation degree and it worked well with my 3D Object.
While rotating my 3D object, my object gets scaled too. I want to stop my scaling while the user is doing the rotation. How can I achieve this? Or how can I pass my both scaling and rotation in a different method to update their respective matrix? I do not want to use any 3D party library for this.
Please help me with this. Below is my code and suggest me where I am doing anything wrong.
ScaleGesture
private class CustomScaleGesture extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
DebugHelper.log("detector.getScaleFactor(): " + detector.getScaleFactor() + " scaleFactor = " + scaleFactor);
scaleFactor *= detector.getScaleFactor();
DebugHelper.log("final scaleFactor: " + scaleFactor);
return true;
}
}
RotationGesture
private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
#Override
public boolean onRotate(RotateGestureDetector detector) {
DebugHelper.log("RotateListener called..");
mRotationDegrees -= detector.getRotationDegreesDelta();
DebugHelper.log("RotateListener: " + mRotationDegrees);
return true;
}
}
MainActivity
public class MyARActivity extends BaseActivity<MyActivityArBinding> implements GLSurfaceView.Renderer {
//AR Variables
private int mWidth;
private int mHeight;
private boolean capturePicture = false;
private boolean installRequested;
private boolean moving;
float[] projmtx = new float[16];
float[] viewmtx = new float[16];
private Session session;
private Snackbar messageSnackbar;
private DisplayRotationHelper displayRotationHelper;
private final BackgroundRenderer backgroundRenderer = new BackgroundRenderer();
private ObjectRenderer virtualObject;
private ObjectRenderer virtualObjectShadow;
private final PlaneRenderer planeRenderer = new PlaneRenderer();
private PointCloudRenderer pointCloud = new PointCloudRenderer();
// Temporary matrix allocated here to reduce number of allocations for each frame.
private float[] anchorMatrix = new float[16];
// Tap handling and UI.
private ArrayBlockingQueue<MotionEvent> queuedSingleTaps = new ArrayBlockingQueue<>(16);
private ArrayList<Anchor> anchors = new ArrayList<>();
//load and manipulate obj
private SQLiteHelper sqlHelper;
private boolean isObjectChanged = false;
private String objectPath;
private List<CharacterModel> characterModelList = new ArrayList<>();
private boolean isFirstTimeLoad = true;
//Gestures
private float mRotationDegrees = 0.f;
private RotateGestureDetector mRotateDetector;
private float scaleFactor = 1.0f;
private ScaleGestureDetector scaleDetector;
private GestureDetector gestureDetector;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHeaderVisible(false);
doDefaults();
}
private void doDefaults() {
binding.setPresenter(this);
sqlHelper = SQLiteHelper.getInstance(this);
load3DCharacters();
initAR();
}
#SuppressLint("ClickableViewAccessibility")
private void initAR() {
displayRotationHelper = new DisplayRotationHelper(this);
scaleDetector = new ScaleGestureDetector(this, new CustomScaleGesture());
mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateListener());
// Set up tap listener.
gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
#Override
public boolean onSingleTapUp(MotionEvent e) {
if (anchors.size() <= 0) {
onSingleTap(e);
}
return true;
}
#Override
public boolean onDown(MotionEvent e) {
return true;
}
});
binding.surfaceView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
DebugHelper.log("binding.surfaceView.setOnTouchListener called..");
mRotateDetector.onTouchEvent(event);
scaleDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
moving = true;
DebugHelper.log("ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
DebugHelper.log("ACTION_UP");
moving = false;
break;
case MotionEvent.ACTION_MOVE:
DebugHelper.log("ACTION_MOVE");
if (anchors.size() > 0) {
onSecondTouch(event);
}
break;
}
return gestureDetector.onTouchEvent(event);
}
});
// Set up renderer.
binding.surfaceView.setPreserveEGLContextOnPause(true);
binding.surfaceView.setEGLContextClientVersion(2);
binding.surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); // Alpha used for plane blending.
binding.surfaceView.setRenderer(this);
binding.surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
installRequested = false;
}
private void onSecondTouch(MotionEvent e) {
Log.e("Second Touch", "Executed");
if (e.getPointerCount() > 1) {
scaleDetector.onTouchEvent(e);
} else {
queuedSingleTaps.offer(e);
}
}
private void onSingleTap(MotionEvent e) {
// Queue tap if there is space. Tap is lost if queue is full.
DebugHelper.log("onSingleTap()");
queuedSingleTaps.offer(e);
}
private void load3DCharacters() {
CharacterModel model = new CharacterModel();
model.setName("Cat");
model.setObjectPath("cat/cat.obj");
model.setScaleFactor(0.25f);
model.setResourceId(R.drawable.cat);
characterModelList.add(model);
model = new CharacterModel();
model.setName("Old Man");
model.setObjectPath("man/muro.obj");
model.setScaleFactor(0.0085f);
model.setResourceId(R.drawable.old_man);
characterModelList.add(model);
model = new CharacterModel();
model.setName("Bloodwing");
model.setObjectPath("bloodwing/bloodwing.obj");
model.setScaleFactor(0.0009f);
model.setResourceId(R.drawable.bat);
characterModelList.add(model);
}
private void loadObject(CharacterModel model) {
try {
this.objectPath = model.getObjectPath();
this.scaleFactor = model.getScaleFactor();
if (virtualObject == null) {
virtualObject = new ObjectRenderer(objectPath);
virtualObject.createOnGlThread(this);
virtualObject.setMaterialProperties(0.0f, 1.0f, 1.0f, 6.0f);
} else {
// Clear screen to notify driver it should not load any pixels from previous frame.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
isObjectChanged = true;
virtualObject.updateObjectPath(model.getObjectPath());
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
#Override
public void onDrawFrame(GL10 gl) {
if (isObjectChanged) {
isObjectChanged = false;
try {
virtualObject.createOnGlThread(this);
virtualObject.setMaterialProperties(0.0f, 2.0f, 0.5f, 6.0f);
} catch (IOException e) {
e.printStackTrace();
}
return;
}
// Clear screen to notify driver it should not load any pixels from previous frame.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
if (session == null) {
return;
}
// Notify ARCore session that the view size changed so that the perspective matrix and
// the video background can be properly adjusted.
displayRotationHelper.updateSessionIfNeeded(session);
try {
session.setCameraTextureName(backgroundRenderer.getTextureId());
// Obtain the current frame from ARSession. When the configuration is set to
// UpdateMode.BLOCKING (it is by default), this will throttle the rendering to the
// camera framerate.
Frame frame = session.update();
Camera camera = frame.getCamera();
// Handle taps. Handling only one tap per frame, as taps are usually low frequency
// compared to frame rate.
MotionEvent tap = queuedSingleTaps.poll();
if (tap != null && camera.getTrackingState() == TrackingState.TRACKING) {
for (HitResult hit : frame.hitTest(tap)) {
// Check if any plane was hit, and if it was hit inside the plane polygon
Trackable trackable = hit.getTrackable();
// Creates an anchor if a plane or an oriented point was hit.
if ((trackable instanceof Plane && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())) || (trackable instanceof Point && ((Point) trackable).getOrientationMode() == Point.OrientationMode.ESTIMATED_SURFACE_NORMAL)) {
// Hits are sorted by depth. Consider only closest hit on a plane or oriented point.
// Cap the number of objects created. This avoids overloading both the
// rendering system and ARCore.
//if (!isUpdate) {
DebugHelper.log("Anchor size = " + anchors.size());
if (anchors.size() >= 1) {
anchors.get(0).detach();
anchors.remove(0);
}
// Adding an Anchor tells ARCore that it should track this position in
// space. This anchor is created on the Plane to place the 3D model
// in the correct position relative both to the world and to the plane.
if (anchors.size() > 0) {
DebugHelper.log("anchor list has data");
for (Anchor anchor : anchors) {
anchor.detach();
anchors.remove(anchor);
}
}
Anchor anchor = hit.createAnchor();
if (anchor != null)
anchors.add(anchor);
else
DebugHelper.log("anchor is null");
//}
break;
}
}
}
// Draw background.
backgroundRenderer.draw(frame);
// If not tracking, don't draw 3d objects.
if (camera.getTrackingState() == TrackingState.PAUSED) {
return;
}
// Get projection matrix.
camera.getProjectionMatrix(projmtx, 0, 0.1f, 100.0f);
// Get camera matrix and draw.
camera.getViewMatrix(viewmtx, 0);
// Compute lighting from average intensity of the image.
final float lightIntensity = frame.getLightEstimate().getPixelIntensity();
// Visualize tracked points.
PointCloud pointCloud = frame.acquirePointCloud();
this.pointCloud.update(pointCloud);
if (!capturePicture)
this.pointCloud.draw(viewmtx, projmtx);
// Application is responsible for releasing the point cloud resources after
// using it.
pointCloud.release();
// Check if we detected at least one plane. If so, hide the loading message.
if (messageSnackbar != null) {
{
for (Plane plane : session.getAllTrackables(Plane.class)) {
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING
&& plane.getTrackingState() == TrackingState.TRACKING) {
hideLoadingMessage();
break;
}
}
}
for (Plane plane : session.getAllTrackables(Plane.class)) {
if (plane.getType() == Plane.Type.HORIZONTAL_UPWARD_FACING && plane.getTrackingState() == TrackingState.TRACKING) {
hideLoadingMessage();
break;
}
}
}
// Visualize planes.
if (!capturePicture)
planeRenderer.drawPlanes(session.getAllTrackables(Plane.class), camera.getDisplayOrientedPose(), projmtx);
// Visualize anchors created by touch.
for (Anchor anchor : anchors) {
if (anchor.getTrackingState() != TrackingState.TRACKING) {
continue;
}
// Get the current pose of an Anchor in world space. The Anchor pose is updated
// during calls to session.update() as ARCore refines its estimate of the world.
anchor.getPose().toMatrix(anchorMatrix, 0);
// Update and draw the model and its shadow.
if (virtualObject != null) {
//passing my scaleFector and rotationDegree to update my matrix.
virtualObject.updateModelMatrix(anchorMatrix, scaleFactor, mRotationDegrees);
if (viewmtx != null && projmtx != null) {
virtualObject.draw(viewmtx, projmtx, lightIntensity);
}
}
}
if (capturePicture) {
capturePicture = false;
onSavePicture();
}
} catch (Throwable t) {
Log.e(TAG, "Exception on the OpenGL thread", t);
}
}
ObjectRenderer
public void draw(float[] cameraView, float[] cameraPerspective, float lightIntensity) {
try {
ShaderUtil.checkGLError(TAG, "Before draw");
Matrix.multiplyMM(mModelViewMatrix, 0, cameraView, 0, mModelMatrix, 0);
Matrix.multiplyMM(mModelViewProjectionMatrix, 0, cameraPerspective, 0, mModelViewMatrix, 0);
Matrix.setRotateM(mRotationMatrix, 0, MyARActivity.rotationDegrees, 0.0f, 1.0f, 0.0f); //rotation degree pass to matrix
Matrix.multiplyMM(mFinalModelViewProjectionMatrix, 0, mModelViewProjectionMatrix, 0, mRotationMatrix, 0);
GLES20.glUseProgram(mProgram);
// Set the lighting environment properties.
Matrix.multiplyMV(mViewLightDirection, 0, mModelViewMatrix, 0, LIGHT_DIRECTION, 0);
normalizeVec3(mViewLightDirection);
GLES20.glUniform4f(mLightingParametersUniform, mViewLightDirection[0], mViewLightDirection[1], mViewLightDirection[2], lightIntensity);
// Set the object material properties.
GLES20.glUniform4f(mMaterialParametersUniform, mAmbient, mDiffuse, mSpecular, mSpecularPower);
// Set the ModelViewProjection matrix in the shader.
GLES20.glUniformMatrix4fv(mModelViewUniform, 1, false, mModelViewMatrix, 0);
GLES20.glUniformMatrix4fv(mModelViewProjectionUniform, 1, false, mModelViewProjectionMatrix, 0);
GLES20.glUniformMatrix4fv(mModelViewProjectionUniform, 1, false, mFinalModelViewProjectionMatrix, 0);
if (mBlendMode != null) {
GLES20.glDepthMask(false);
GLES20.glEnable(GLES20.GL_BLEND);
switch (mBlendMode) {
case Shadow:
// Multiplicative blending function for Shadow.
GLES20.glBlendFunc(GLES20.GL_ZERO, GLES20.GL_ONE_MINUS_SRC_ALPHA);
break;
case Grid:
// Grid, additive blending function.
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
break;
}
}
if (mObj != null && mObj.getNumMaterialGroups() > 0) {
//Start drawing data from each VAO
for (int i = 0; i < mObj.getNumMaterialGroups(); i++) {
// Attach the object texture.
GLES20.glUniform1i(mTextureUniform, 0);
GLES20.glBindTexture(GL_TEXTURE_2D, mTextures[i]);
GLES30.glBindVertexArray(vectorArrayObjectIds[i]);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, mObj.getMaterialGroup(i).getNumFaces() * 3,
GLES20.GL_UNSIGNED_SHORT, 0);
GLES30.glBindVertexArray(0);
//Unbind texture
GLES20.glBindTexture(GL_TEXTURE_2D, 0);
}
}
if (mBlendMode != null) {
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glDepthMask(true);
}
ShaderUtil.checkGLError(TAG, "After draw");
} catch (Exception ex) {
ex.printStackTrace();
}
}
I am using Scene2D.
This is my Shoot Actor
public class ActorDisparo extends Actor {
private TextureRegion textureDisparo = new TextureRegion();
private World world;
public static Body body;
//private Fixture fixture;
private FixtureDef fdef;
private String userData;
private float anchoDisparo, altoDisparo;
private float positionX,positionY;
private static float porcentajeDeancho = 0,anchoMediano = 0, anchoPequeno = 0, anchoDiminuto = 0;
private TextureRegion arponRegion = new TextureRegion();
public ActorDisparo(World world, TextureRegion disparo, Vector2 position, float anchoDisparo, float altoDisparo, float fuerzaX, float fuerzaY, String userData){
this.textureDisparo = disparo;
this.world = world;
this.anchoDisparo = anchoDisparo;
this.altoDisparo = altoDisparo;
this.userData = userData;
BodyDef bodyDef = new BodyDef();
bodyDef.position.set(position);
bodyDef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bodyDef);
PolygonShape shapeDisparo = new PolygonShape();
shapeDisparo.setAsBox((anchoDisparo/2)/2,altoDisparo/2);
fdef = new FixtureDef();
fdef.shape = shapeDisparo;
//fdef.restitution = 0.70f;
fdef.filter.categoryBits = BolasGame.DISPARO_BIT;
fdef.filter.maskBits = BolasGame.BOLAROJA_BIT |
BolasGame.TECHO_BIT;
body.createFixture(fdef).setUserData(userData);
shapeDisparo.dispose();
body.setGravityScale(0f);
body.applyLinearImpulse(fuerzaX,fuerzaY,position.x,position.y,true);
setSize( anchoDisparo *BolasGame.MetrosAPixels ,altoDisparo*BolasGame.MetrosAPixels);
//setBounds(0, 0, getWidth(), getHeight());
}
public float getPositionX() {
return getPositionX();
}
public float getPositionY() {
return getPositionY();
}
#Override
public void act(float delta) {
super.act(delta);
}
#Override
public void draw(Batch batch, float parentAlpha) {
super.draw(batch, parentAlpha);
setPosition( (body.getPosition().x * BolasGame.MetrosAPixels) - getWidth()/2, (body.getPosition().y * BolasGame.MetrosAPixels) - getHeight()/2);
batch.draw(textureDisparo,getX(),getY(),getWidth(),getHeight());
}
public void detach(){
world.destroyBody(body);
remove();
}
}
and this is the call to shoot class
if(Gdx.input.justTouched() && hasdisparado == false){
hasdisparado =true;
touch = new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0);
stage.getCamera().unproject(touch);
arponRegion = new TextureRegion(textureDisparo, 0, 0, 20, 40);
actorDisparo = new ActorDisparo(world,arponRegion,new Vector2(touch.x/BolasGame.MetrosAPixels,touch.y/BolasGame.MetrosAPixels ),20/BolasGame.MetrosAPixels,40/BolasGame.MetrosAPixels,0,2f,BolasGame.USERDATA_DISPARO);
stage.addActor(actorDisparo);
}
this creates a shot like a bullet but I need since I touched the screen until it is destroyed is continuous like the harpoon of game pang
the textureregion the charge of a texture whose dimensions are 446x20
textureDisparo = new Texture("gfx/disparo.png");
I have no idea how to do this because you have to repeat the image and body in box2d for collisions.
I totally lost
if someone can give me an idea would appreciate
Thank you
(Sorry for my English, I'm learning now)
I recently started using the Polygons but I'm have struggle as I'm trying to make a Polygon move towards the center of my screen, although I don't know how.
Here's the code I have(What I'm trying to accomplish here is to make the balls spawn randomly by every side of the screen and make them go towards the middle of the screen to collide against a square):
public class Game extends com.badlogic.gdx.Game implements Screen{
private SpriteBatch batch;
private Texture Ball;
private Texture Up;
private OrthographicCamera cam;
private int score;
private String showScore;
private BitmapFont scoreFont;
private Sprite upSprite;
private Polygon square;
private Array<Polygon> balls1;
private Array<Polygon> balls2;
private Array<Polygon> balls3;
private Array<Polygon> balls4;
private Polygon ball1 = new Polygon();
private Polygon ball2 = new Polygon();
private Polygon ball3 = new Polygon();
private Polygon ball4 = new Polygon();
#Override
public void create(){
batch = new SpriteBatch();
Ball = new Texture("energyball.png");
Up = new Texture("up.png");
upSprite = new Sprite(Up);
upSprite.setOriginCenter();
upSprite.setX(615);
upSprite.setY(340);
upSprite.setRegionWidth(64);
upSprite.setRegionHeight(64);
square = new Polygon(new float[] {
upSprite.getX(), upSprite.getY(),
upSprite.getX(), upSprite.getY() + upSprite.getHeight(),
upSprite.getX() + upSprite.getWidth(), upSprite.getY() + upSprite.getY(),
upSprite.getX() + upSprite.getWidth(), upSprite.getY()
});
cam = new OrthographicCamera();
cam.setToOrtho(false, 1280, 720);
upSprite.setPosition(upSprite.getX(), upSprite.getY());
square.setOrigin(upSprite.getX(), upSprite.getY());
// calls the functions to spawn balls randomly
balls1 = new Array<Polygon>();
balls2 = new Array<Polygon>();
balls3 = new Array<Polygon>();
balls4 = new Array<Polygon>();
score();
//if the screen is touched sprite rotates 90 degrees clockwise
Gdx.input.setInputProcessor(new InputAdapter() {
#Override
public boolean touchDown(int x, int y, int pointer, int button) {
return true;
}
#Override
public boolean touchUp(int x, int y, int pointer, int button) {
upSprite.rotate(-90);
square.rotate(-90f);
return true;
}
});
}
//shows score
private void score() {
score = -4;
showScore = "Score: 0";
scoreFont = new BitmapFont();
}
//creates the balls and sets their position as well as the random timer for each
private void spawnBalls1() {
ball1.setPosition(MathUtils.random(639, 641), 720);
ball1.setScale(32, 32);
balls1.add(ball1);
lastDropTime = TimeUtils.nanoTime();
}
private void spawnBalls2() {
ball2.setPosition(0, MathUtils.random(359, 361));
ball2.setScale(32, 32);
balls2.add(ball2);
lastDropTime = TimeUtils.nanoTime();
}
private void spawnBalls3() {
ball3.setPosition(MathUtils.random(639, 641), 0);
ball3.setScale(32, 32);
balls3.add(ball3);
lastDropTime = TimeUtils.nanoTime();
}
private void spawnBalls4() {
ball4.setPosition(1280, MathUtils.random(359, 361));
ball4.setScale(32, 32);
balls4.add(ball4);
lastDropTime = TimeUtils.nanoTime();
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
//draws the game itself as well as the balls on the screen
batch.setProjectionMatrix(cam.combined);
batch.begin();
upSprite.draw(batch);
//draws the balls
for (Polygon ball1 : balls1) {
batch.draw(Ball, ball1.getX(), ball1.getY());
}
for (Polygon ball2 : balls2) {
batch.draw(Ball, ball2.getX(), ball2.getY());
}
for (Polygon ball3 : balls3) {
batch.draw(Ball, ball3.getX(), ball3.getY());
}
for (Polygon ball4 : balls4) {
batch.draw(Ball, ball4.getX(), ball4.getY());
}
scoreFont.setColor(1, 1, 1, 1);
scoreFont.draw(batch, showScore, 25, 100);
batch.end();
// if the time minus the time of the last ball spawn is less than x then spawn another ball in a random place
if (TimeUtils.nanoTime() - lastDropTime > 1000000000) {
switch (MathUtils.random(4)) {
case 0:
spawnBalls1();
break;
case 1:
spawnBalls2();
break;
case 2:
spawnBalls3();
break;
case 3:
spawnBalls4();
break;
}
}
Iterator<Polygon> iter1 = balls1.iterator();
while(iter1.hasNext()) {
Polygon balls1 = iter1.next();
balls1.getY() -= 350 * Gdx.graphics.getDeltaTime();
if (Intersector.overlapConvexPolygons(balls1, square)) {
score++;
showScore = "Score: " + score;
iter1.remove();
}
}
Iterator<Polygon> iter2 = balls2.iterator();
while (iter2.hasNext()) {
Polygon balls2 = iter2.next();
balls2.x += 550 * Gdx.graphics.getDeltaTime();
if (Intersector.overlapConvexPolygons(balls2, square)) {
score++;
showScore = "Score: " + score;
iter2.remove();
}
}
Iterator<Polygon> iter3 = balls3.iterator();
while(iter3.hasNext()) {
Polygon balls3 = iter3.next();
balls3.y += 350 * Gdx.graphics.getDeltaTime();
if (Intersector.overlapConvexPolygons(balls3, square)) {
score++;
showScore = "Score: " + score;
iter3.remove();
}
}
Iterator<Polygon> iter4 = balls4.iterator();
while(iter4.hasNext()) {
Polygon balls4 = iter4.next();
balls4.x -= 550 * Gdx.graphics.getDeltaTime();
if (Intersector.overlapConvexPolygons(balls4, square)) {
score++;
showScore = "Score: " + score;
iter4.remove();
}
}
}
And I'm getting an error here:
balls1.getY() -= 350 * Gdx.graphics.getDeltaTime();
balls2.getX() += 550 * Gdx.graphics.getDeltaTime();
balls3.getY() += 350 * Gdx.graphics.getDeltaTime();
balls4.getX() -= 550 * Gdx.graphics.getDeltaTime();
Any help would be gladly appreciated.
I tried almost everything but nothing works.
If you run it you will see a square falling down very slow.
I'm a beginner so explain it not too complicated.
Any questions about this code dylan.missu#gmail.com
Here is my code:
#Override
public void show() {
camera = new OrthographicCamera();
debugRenderer = new Box2DDebugRenderer();
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyDef.BodyType.DynamicBody;
bodyDef.position.set(100,100);
body = world.createBody(bodyDef);
PolygonShape shape = new PolygonShape();
shape.setAsBox(Gdx.graphics.getHeight()/6,Gdx.graphics.getHeight()/6);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = shape;
fixtureDef.density = 1f;
fixtureDef.restitution = 2;
Fixture fixture = body.createFixture(fixtureDef);
}
private void line(float X, float Y, float w, float h)
{
BodyDef bodyDef = new BodyDef();
bodyDef.type=BodyDef.BodyType.StaticBody;
bodyDef.position.set(X,Y);
PolygonShape polygonShape=new PolygonShape();
polygonShape.setAsBox(w,h);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape=polygonShape;
fixtureDef.restitution=0.4f;
fixtureDef.friction=0.5f;
Body theFloor=world.createBody(bodyDef);
theFloor.createFixture(fixtureDef);
}
private void wall(float A)
{
// is there a better way of doing this?
line(0,Gdx.graphics.getHeight()/2-A,Gdx.graphics.getWidth()/2-A,0);
line(Gdx.graphics.getWidth()/2-A,0,0,Gdx.graphics.getHeight()/2-A);
line(0,-Gdx.graphics.getHeight()/2+A,Gdx.graphics.getWidth()/2-A,0);
line(-Gdx.graphics.getWidth()/2+A,0,0,Gdx.graphics.getHeight()/2-A);
}
#Override
public void render(float delta) {
world.step(1 / 5f, 6, 2);
OrthographicCamera camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
processAccelerometer();
wall(1);
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
debugRenderer.render(world, camera.combined);
}
#Override
public void dispose() {
world.dispose();
debugRenderer.dispose();
}
private void processAccelerometer() {
float y = Gdx.input.getAccelerometerY();
float x = Gdx.input.getAccelerometerX();
if (prevAccelX != x || prevAccelY != y) {
world.setGravity(new Vector2(y, -x));
prevAccelX = x;
prevAccelY = y;
}
}
#Override
public void hide()
{
// TODO: Implement this method
}
#Override
public void resize(int p1, int p2)
{
// TODO: Implement this method
}
#Override
public void resume()
{
// TODO: Implement this method
}
#Override
public void pause()
{
// TODO: Implement this method
}
#Override
public void render()
{
// TODO: Implement this method
}
}
In Box2D you have to take into account that 1 pixel = 1 meter. So, basically, everything you simulate in Box2D will be HUGE by default. And Box2D simulations are not accurate for huge distances, huge masses, huge speeds...
So all you have to do is convert your viewport with a conversion factor, so you'll just look at small entities, that will be easier to simulate.
For example, let's say you want 100 pixel = 1 meter, you'll put that code when you create your game screen :
WORLD_TO_BOX = 1/100f;
BOX_TO_WORLD = 1/WORLD_TO_BOX;
//Creation of the camera with your factor 100
camera = new OrthographicCamera();
camera.viewportHeight = Gdx.graphics.getHeight() * WORLD_TO_BOX;
camera.viewportWidth = Gdx.graphics.getWidth() * WORLD_TO_BOX;
camera.position.set(camera.viewportWidth/2, camera.viewportHeight/2, 0f);
camera.update();
Then, you'll create your boxes in term of camera.viewportWidth and camera.viewportHeight, instead of Gdx.graphics.getWidth() and Gdx.graphics.getHeight()
I your case you'll have :
PolygonShape shape = new PolygonShape();
shape.setAsBox(camera.viewportHeight/6,camera.viewportHeight/6);
You can see in my code, there is also a BOX_TO_WORLD conversion factor. It will be used when you want to render graphics over your box2D bodies.
For that, look at the answer of this question.
I'm using this to create a player body
private void addPlayer(){
// viral -- add player
final float startX = (CAMERA_WIDTH - this.mPlayerTextureRegion
.getWidth()) / 2;
final float startY = CAMERA_HEIGHT + 100;
this.playerFace = new Sprite(startX, startY, this.mPlayerTextureRegion,
this.getVertexBufferObjectManager());
this.playerBody = PhysicsFactory.createCircleBody(this.mPhysicsWorld,
playerFace, BodyType.DynamicBody, FIXTURE_DEF);
final PhysicsHandler physicsHandler = new PhysicsHandler(playerFace);
playerFace.registerUpdateHandler(physicsHandler);
playerBody.setBullet(true);
playerFaceHalfWidth = playerFace.getWidth() / 2;
playerFaceHalfHeight = playerFace.getHeight() / 2;
this.mScene.attachChild(playerFace);
playerFace.setY(CAMERA_HEIGHT - playerFaceHalfHeight * 2);
}
This is how I'm detecting collision with other sprites:
protected void onManagedUpdate(float pSecondsElapsed) {
if (playerFace.collidesWith(this)) {
// safe of touching border - else game over
if (!isPlayerInSafeZone()) {
Log.w("cd", "Game Over");
isGameInProgress = false;
mScene.setIgnoreUpdate(true);
processLevelEnd();
}
}
};
The issue now is that even though i've added a circle body, the collision is detected with the player when the other sprites touch the corner of the png for player. it is a transparent area and the image has only a circle in it for which the body was created. Why is this happening? What is wrong in the above code?
::Update based on Lucaz's inputs:
This is my contactlistener
private ContactListener createContactListener()
{
ContactListener contactListener = new ContactListener()
{
#Override
public void beginContact(Contact contact)
{
final Fixture x1 = contact.getFixtureA();
final Fixture x2 = contact.getFixtureB();
final String x1id = (String) x1.getBody().getUserData();
final String x2id = (String) x2.getBody().getUserData();
Log.w("cd", "x1 = " + x1id + " --- x2 = " + x2id);
}
#Override
public void endContact(Contact contact)
{
}
#Override
public void preSolve(Contact contact, Manifold oldManifold)
{
}
#Override
public void postSolve(Contact contact, ContactImpulse impulse)
{
}
};
return contactListener;
}
This is how I'm setting the user data for the player and other sprites:
Player:
this.playerFace = new Sprite(startX, startY, this.mPlayerTextureRegion,
this.getVertexBufferObjectManager());
this.playerBody = PhysicsFactory.createCircleBody(this.mPhysicsWorld,
playerFace, BodyType.DynamicBody, FIXTURE_DEF);
playerBody.setBullet(true);
playerBody.setUserData("player");
other sprites (balls):
body = PhysicsFactory.createCircleBody(this.mPhysicsWorld, face,
BodyType.DynamicBody, FIXTURE_DEF);
body.setLinearVelocity(vector2);
body.setBullet(true);
body.setUserData("ball");
This is how i added the listener to physics world
this.mPhysicsWorld.setContactListener(createContactListener());
The listener is written to print the user data for colliding objects:
but when a sprite collides with player i get null for the player but user data for the other sprite as shown below... how to fix this?
x1 = null --- x2 = ball
its because collidesWith() method is checking collisions of sprites, and not bodies. So it checks if two sprites overlap (even if they are png images, they will stll be treated as not transparent rectangles). To check collisions between bodies there is another way:
private ContactListener createContactListener()
{
ContactListener contactListener = new ContactListener()
{
#Override
public void beginContact(Contact contact)
{
final Fixture x1 = contact.getFixtureA();
final Fixture x2 = contact.getFixtureB();
}
#Override
public void endContact(Contact contact)
{
}
#Override
public void preSolve(Contact contact, Manifold oldManifold)
{
}
#Override
public void postSolve(Contact contact, ContactImpulse impulse)
{
}
};
return contactListener;
}
Please look this simple tutorial:
http://www.matim-dev.com/handling-collisions-between-bodies.html