I want to limit the area to move the sprite object only on this area (for example a area dimensions 200x200).
I would to create a box2D 200x200 where the sprites can moved only on this area
How do you do that please?
#Override
public Scene onCreateScene() {
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene scene = new Scene();
scene.setBackground(new Background(0.09804f, 0.6274f, 0.8784f));
final float centerX = (CAMERA_WIDTH - this.mFaceTextureRegionLetterOne
.getWidth()) / 2;
final float centerY = (CAMERA_HEIGHT - this.mFaceTextureRegionLetterOne
.getHeight()) / 2;
final Sprite letterOne = new Sprite(centerX - centerX / 2, centerY
- centerY / 2, this.mFaceTextureRegionLetterOne,
this.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2,
pSceneTouchEvent.getY() - this.getHeight() / 2);
return true;
}
};
final Sprite letterTwo = new Sprite(centerX - centerX / 2, centerY,
this.mFaceTextureRegionLetterTwo,
this.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent,
final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2,
pSceneTouchEvent.getY() - this.getHeight() / 2);
//int count = scene.getChildCount();
//for(int i = 0; i < count; i++) {
IEntity entity = scene.getChildByIndex(1);
if (entity instanceof Sprite) {
if (entity.getUserData().equals("sprite"))
if (((Sprite) entity).collidesWith(letterOne))
Log.v("colission", "face_box is collised on google plus -> letterTwo on letterOne");
}
//}
return true;
}
};
letterTwo.setUserData("sprite");
final Sprite boxArea = new Sprite(centerX, centerY,
this.mFaceTextureRegionBox, this.getVertexBufferObjectManager());
letterOne.setScale(2);
scene.attachChild(letterOne);
scene.registerTouchArea(letterOne);
letterTwo.setScale(2);
scene.attachChild(letterTwo);
scene.registerTouchArea(letterTwo);
boxArea.setScale(2);
scene.attachChild(boxArea);
scene.setTouchAreaBindingOnActionDownEnabled(true);
return scene;
}
Thank you.
I don't know box2D, but normally you would check the edges of the sprite on each movement, if they do not overlap the edges of the area.
If your sprite is represented by a Rectangle, and the area where you can move also represented by a Rectangle, this could be done easy.
But first, check the box2D API, maybe it has already some helper methods to ease this task, something like:
obect.overlaps(object)
You should create a Rectangle of 200X200 and then you should use create a body of the rectangle using the Box2D Physics. And for Fixture definition for rectangle you can set density, Elasticity and Friction so that your sprites will be treated accordingly when they collide with the boundary of the Rectangle.
To create a Rectangle you can refer the Examples of the Andengine.
With your code, you seem to try to do collision checking with your sprites ?!
It would be complicated and not nice when you try to do things without physics bodies. So lets use the physics bodies with your sprites.
But note that, DO NOT create a solid box body for your area (containing your sprites), lets use 4 separated body walls (left, top, right, bottom) to form a closed box; because game engine can only check collision with solid shapes.
The following is the code for reference:
/**
* #param pScene
* Sence of the game, get from class member
* #param pWorld
* physics world of the game, get from class member
*/
public void CreateSprites(final Scene pScene, final PhysicsWorld pWorld)
{
final FixtureDef mFixtureDef = PhysicsFactory.createFixtureDef(10, 1.1f, 0.0f);//should be placed as member of class
final Sprite letterTwo = new Sprite(centerX - centerX / 2, centerY,
this.mFaceTextureRegionLetterTwo,
this.getVertexBufferObjectManager());
final Body letterTwoBody = PhysicsFactory.createBoxBody(pWorld, letterTwo, BodyType.DynamicBody, mFixtureDef);
letterTwo.setUserData(letterTwoBody); // for later sprite-body attachment access
pScene.attachChild(letterTwo);
pWorld.registerPhysicsConnector(new PhysicsConnector(letterTwo, letterTwoBody, true, true));
}
/** Create the walls, in these boudaries sprites will move */
public void InitBoxWalls(Scene pScene, PhysicsWorld pWorld)
{
final float WALL_MARGIN_WIDTH = 5f;
final float WALL_MARGIN_HEIGHT = 10f;
final VertexBufferObjectManager vertexBufferObjectManager = this.getVertexBufferObjectManager();
mWallGround = new Rectangle(-WALL_MARGIN_WIDTH, mCameraHeight + WALL_MARGIN_HEIGHT - 2, mCameraWidth + 2*WALL_MARGIN_WIDTH, 2, vertexBufferObjectManager);
mWallRoof = new Rectangle(-WALL_MARGIN_WIDTH, -WALL_MARGIN_HEIGHT, mCameraWidth + 2*WALL_MARGIN_WIDTH, 2, vertexBufferObjectManager);
mWallLeft = new Rectangle(-WALL_MARGIN_WIDTH, -WALL_MARGIN_HEIGHT, 2, mCameraHeight + 2*WALL_MARGIN_HEIGHT, vertexBufferObjectManager);
mWallRight = new Rectangle(mCameraWidth + WALL_MARGIN_WIDTH - 2, -WALL_MARGIN_HEIGHT, 2, mCameraHeight + 2*WALL_MARGIN_HEIGHT, vertexBufferObjectManager);
PhysicsFactory.createBoxBody(pWorld, mWallGround, BodyType.StaticBody, mWallFixtureDef);
PhysicsFactory.createBoxBody(pWorld, mWallRoof, BodyType.StaticBody, mWallFixtureDef);
PhysicsFactory.createBoxBody(pWorld, mWallLeft, BodyType.StaticBody, mWallFixtureDef);
PhysicsFactory.createBoxBody(pWorld, mWallRight, BodyType.StaticBody, mWallFixtureDef);
pScene.attachChild(mWallGround);
pScene.attachChild(mWallRoof);
pScene.attachChild(mWallLeft);
pScene.attachChild(mWallRight);
}
And the last thing you should do is to look up example of Physics/MouseJoint in AndEngine examples.
Related
I am developing an android app which visualize the map of an environment and currently i am using libgdx to draw the map, also like any map application the user should be capable of zoom, rotate and moving the map,
I have developed a GestureHandler class which implements GestureListener interface and interacts with a PerspectiveCamera(since i will use 3d components in the future):
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
float tempX = (mapView.getCamera().position.x - deltaX * 0.5f);
float tempY = (mapView.getCamera().position.y + deltaY * 0.5f);
mapView.getCamera().position.set(
MathUtils.lerp(mapView.getCamera().position.x, tempX, mapView.getCamera().fieldOfView / 100),
MathUtils.lerp(mapView.getCamera().position.y, tempY, mapView.getCamera().fieldOfView / 100),
mapView.getCamera().position.z);
mapView.getCamera().update();
return false;
}
float initialDistance = 0;
float initialAngle = 0;
float distance = 0;
private void zoom(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2)
{
initialDistance = initialPointer1.dst(initialPointer2);
float iDeltaX = initialPointer2.x - initialPointer1.x;
float iDeltaY = initialPointer2.y - initialPointer1.y;
initialAngle = (float)Math.atan2((double)iDeltaY,(double)iDeltaX) * MathUtils.radiansToDegrees;
if(initialAngle < 0)
initialAngle = 360 - (-initialAngle);
distance = initialPointer1.dst(pointer2);
float deltaX = pointer2.x - initialPointer1.x;
float deltaY = pointer2.y - initialPointer1.y;
newAngle = (float)Math.atan2((double)deltaY,(double)deltaX) * MathUtils.radiansToDegrees;
if(newAngle < 0)
newAngle = 360 - (-newAngle);
//Log.e("test", distance + " " + initialDistance);
//Log.e("test", newAngle + " " + initialAngle);
float ratio = initialDistance/distance;
mapView.getCamera().fieldOfView = MathUtils.clamp(initialZoomScale * ratio, 1f, 100.0f);
Log.e("zoom", String.valueOf(mapView.getCamera().fieldOfView));
mapView.getCamera().update();
}
#Override
public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) {
zoom(initialPointer1, initialPointer2, pointer1, pointer2);
float delta1X = pointer2.x - pointer1.x;
float delta1Y = pointer2.y - pointer1.y;
newAngle = (float)Math.atan2((double)delta1Y,(double)delta1X) * MathUtils.radiansToDegrees;
if(newAngle < 0)
newAngle = 360 - (-newAngle);
System.out.println("new "+newAngle);
if(newAngle - currentAngle >= 0.01000f)
{
System.out.println("Increasing");
mapView.getCamera().rotate(0.5f,0,0,1);
}
else if(newAngle - currentAngle <= -0.010000f) {
System.out.println("DEcreasing");
mapView.getCamera().rotate(-0.5f,0,0,1);
}
if(Math.abs(newAngle - currentAngle) >= 0.01000f)
{
currentAngle = newAngle;
}
return true;
}
Everything is fine until as far as i don't rotate the camera, just like this unsolved similar question after rotating the camera, movements will be affected by applied rotation.Any help specially sample codes?
Edit:
After lots of efforts i finally solved it,
As Tenfour04 said in his answer i had to use two separate matrices for transformation and rotations, and finally set the result of their multiplication to view matrix of camera using:
camera.view.set(position).mul(orientation);
Also the most important thing is to set the Transformation Matrix of my batch to camera.view:
batch.setTransformationMatrix(camera.view)
Instead of applying the gestures directly to the camera, apply them to a pair of Matrix4's that you use to store the orientation and position separately. Then in the render method, multiply the two matrices and apply them to your camera's view.
In the render() method:
camera.view.set(orientation).mul(position); //Might need to swap orientation/position--don't remember.
camera.update();
Your zoom method is fine because field of view affects the camera's projection matrix rather than its view matrix.
i am trying to study andengine. When i am touching on center of sprite(tree) everything works fine. But when i am touching on top of the sprite it jump to the center of the sprite.
hope my question is clear.
this is the on touch event code i am using
final Sprite face = new Sprite(centerX, centerY, this.mFaceTextureRegion, this.getVertexBufferObjectManager()) {
#Override
public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
this.setPosition(pSceneTouchEvent.getX() - this.getWidth() / 2, pSceneTouchEvent.getY() - this.getHeight() / 2);
return true;
}
};
I think you want your sprite to center of the Scene when you touch it on any part of sprite,
But you are using onAreaTouched for that use should use IOnSceneTouchListener for that like below :-
#Override
public boolean onSceneTouchEvent(final Scene pScene,
final TouchEvent pSceneTouchEvent) {
this.setPosition(pSceneTouchEvent.getX() - face.getWidth() / 2, pSceneTouchEvent.getY() - face.getHeight() / 2);
return false;
}
I'm playing around in AndEngine and learning this non-documented while making a splashscreen. I'm aware there's a class SplashScene, but as I'm learning I'm trying all kind of ways.
However, I can't seem to get this one right. The screen is 240x320 (W x H) and splash screen texture is 480x640 so i'm scaling it down to fit the screen. Texture loading etc is working fine, but when the sprite is shown I first see the large texture for 0.1secs, then it gets scaled down. I want it to get scaled down prior shown. Been trying everything, moved the call to attachChild() to onLoadComplete(), using setVisible(false), etc but I see the texture getting scaled down everytime.
Why?
Here's my code:
#Override
public Scene onLoadScene() {
this.scene = new Scene();
// Texture sizes
final int sX = mSplashTextureRegion.getWidth();
final int sY = mSplashTextureRegion.getHeight();
// Center on camera
final int cX = (CAMERA_WIDTH - sX) / 2;
final int cY = (CAMERA_HEIGHT - sY) / 2;
// Scale factor according to camera
final float scaleFactor = Math.min((float) CAMERA_WIDTH / sX, (float) CAMERA_HEIGHT / sY);
// Init sprite
splashScreen = new Sprite(cX, cY, mSplashTextureRegion);
splashScreen.setVisible(false);
// Rescale the splash-screen to fit the display, move it to (0, 0) and show it.
splashScreen.registerEntityModifier(new ScaleModifier(0.1f, 1.0f, scaleFactor));
//splashScreen.registerEntityModifier(new ScaleAtModifier(0.001f, 1.0f, scaleFactor, 0, 0));
// splashScreen.registerEntityModifier(new SequenceEntityModifier(
// new ScaleModifier(0.1f, 1.0f, scaleFactor),
// new DelayModifier(0.2f)
// ));
return scene;
}
#Override
public void onLoadComplete() {
scene.attachChild(splashScreen);
splashScreen.setVisible(true);
}
If I rewrite onLoadComplete() in to this:
#Override
public void onLoadComplete() {
scene.attachChild(splashScreen);
mHandler.postDelayed(korv, 1000);
}
private Runnable korv = new Runnable() {
#Override
public void run() {
splashScreen.setVisible(true);
}
};
the flicker is gone, but that doesn't feel like a good solution.
ScaleModifier adjust the scale over the specified time factor - in your case 0.1f
ScaleModifier(0.1f, 1.0f, scaleFactor)
What you probably want is to scale the Sprite directly using
SplashScreen.setScale(scaleFactor);
do that before attaching the child and you should be fine.
Ok, this should be simple enough, but I'm tripping myself up on the math. Using AndEngine BTW>
I'm using some of the tutorials out there... hero on the left of the screen (landscape) shooting right. Everything works wonderfully. Now I'd like to have the hero on the right side of the screen shooting left. I'm going in circles and would great appreciate some help. Here is the code I'm using for left hero, shooting right.
/** shoots a projectile from the player's position along the touched area */
private void shootProjectile(final float pX, final float pY) {
int offX = (int) (pX - (hero.getX()));
int offY = (int) (pY - (hero.getY() + hero.getHeight()/2));
if (offX <= 0) return;
// position the projectile on the player and set up path
projectile = pPool.obtainPoolItem();
int realX = (int) (mCamera.getWidth() - (hero.getX() ) );
float ratio = (float) realX / (float) offX;
int realY = (int) ((offY * ratio));
float length = (float) Math.sqrt((realX * realX) + (realY * realY));
float velocity = 280.0f / .5f; // 480 pixels per (sec)f on screen
float realMoveDuration = length / velocity;
// defining a moveBymodifier from the projectile's position to the
// calculated one
//this code angles the projectile sprite
double PI = 3.14159265;
float dx = pX - hero.getX();
float dy = pY - hero.getY()-50;
double Radius = Math.atan2(dy,dx);
double Angle = Radius * 180 / PI;
projectile.setRotation((float)Angle); // sets the angle of the projectile
//Move modifier for projectile
MoveByModifier movMByod = new MoveByModifier(realMoveDuration, realX, realY);
final ParallelEntityModifier par = new ParallelEntityModifier(movMByod);
DelayModifier dMod = new DelayModifier(0.001f);
dMod.addModifierListener(new IModifierListener<IEntity>() {
#Override
public void onModifierStarted(IModifier<IEntity> arg0, IEntity arg1) {
}
#Override
public void onModifierFinished(IModifier<IEntity> arg0, IEntity arg1) {
// TODO Auto-generated method stub
shootingSound.play();
projectile.setVisible(true);
projectile.setPosition(hero.getX(), hero.getY() + hero.getHeight() / 2);
projectilesToBeAdded.add(projectile);
projectile.animate(50);
}
});
SequenceEntityModifier seq = new SequenceEntityModifier(dMod, par);
projectile.registerEntityModifier(seq);
projectile.setVisible(false);
mMainScene.attachChild(projectile, 1);
I've got the hero positioned fine on the right side. What do I need to do to get the projectile to move to the left correctly?
Thanks a ton for any help.
MWM
You shouldn't use DelayModifier the way you do. Instead create a PhysicsHandler for your sprites and then set velocity to the PhysicsHandler. Something like:
PhysicsHandler phys = new PhysicsHandler();
projectile.registerUpdateHandler(phys);
phys.setVelocityX(50);
and this will take care of moving your projectile. You can also set acceleration on the physics handler the same way. So if you set the initial velocity to point up and left and then set the acceleration pointing down, the projectile will first fly left and up and then gradually fall down. And you don't have to do any calculations yourself.
This code looks like the one from http://jimmaru.wordpress.com/2011/09/28/andengine-simple-android-game-tutorial/
if it is, try this:
private void shootProjectile(final float pX, final float pY) {
int side = 1;
int offX = (int) (pX - (hero.getX()));
int offY = (int) (pY - (hero.getY() + hero.getHeight()/2));
if (offX <= 0){
side=-1
}
// position the projectile on the player and set up path
projectile = pPool.obtainPoolItem();
int realX = (int) (mCamera.getWidth() - (hero.getX() ) ) * side;
....
I got the same problem with the code from link, with this change i could shoot fot both sides with a player in the middle of screen.
So I have an ImageView using a Matrix to scale the Bitmap I'm displaying. I can double-tap to zoom to full-size, and my ScaleAnimation handles animating the zoom-in, it all works fine.
Now I want to double-tap again to zoom out, but when I animate this with ScaleAnimation, the ImageView does not draw the newly exposed areas of the image (as the current viewport shrinks), instead you see the portion of visible image shrinking in. I have tried using ViewGroup.setClipChildren(false), but this only leaves the last-drawn artifacts from the previous frame - leading to an trippy telescoping effect, but not quite what I was after.
I know there are many zoom-related questions, but none cover my situation - specifically animating the zoom-out operation. I do have the mechanics working - ie aside from the zoom-out animation, double-tapping to zoom in and out works fine.
Any suggestions?
In the end I decided to stop using the Animation classes offered by Android, because the ScaleAnimation applies a scale to the ImageView as a whole which then combines with the scale of the ImageView's image Matrix, making it complicated to work with (aside from the clipping issues I was having).
Since all I really need is to animate the changes made to the ImageView's Matrix, I implemented the OnDoubleTapListener (at the end of this post - I leave it as an "exercise to the reader" to add the missing fields and methods - I use a few PointF and Matrix fields to avoid excess garbage creation). Basically the animation itself is implemented by using View.post to keep posting a Runnable that incrementally changes the ImageView's image Matrix:
public boolean onDoubleTap(MotionEvent e) {
final float x = e.getX();
final float y = e.getY();
matrix.reset();
matrix.set(imageView.getImageMatrix());
matrix.getValues(matrixValues);
matrix.invert(inverseMatrix);
doubleTapImagePoint[0] = x;
doubleTapImagePoint[1] = y;
inverseMatrix.mapPoints(doubleTapImagePoint);
final float scale = matrixValues[Matrix.MSCALE_X];
final float targetScale = scale < 1.0f ? 1.0f : calculateFitToScreenScale();
final float finalX;
final float finalY;
// assumption: if targetScale is less than 1, we're zooming out to fit the screen
if (targetScale < 1.0f) {
// scaling the image to fit the screen, we want the resulting image to be centred. We need to take
// into account the shift that is applied to zoom on the tapped point, easiest way is to reuse
// the transformation matrix.
RectF imageBounds = new RectF(imageView.getDrawable().getBounds());
// set up matrix for target
matrix.reset();
matrix.postTranslate(-doubleTapImagePoint[0], -doubleTapImagePoint[1]);
matrix.postScale(targetScale, targetScale);
matrix.mapRect(imageBounds);
finalX = ((imageView.getWidth() - imageBounds.width()) / 2.0f) - imageBounds.left;
finalY = ((imageView.getHeight() - imageBounds.height()) / 2.0f) - imageBounds.top;
}
// else zoom around the double-tap point
else {
finalX = x;
finalY = y;
}
final Interpolator interpolator = new AccelerateDecelerateInterpolator();
final long startTime = System.currentTimeMillis();
final long duration = 800;
imageView.post(new Runnable() {
#Override
public void run() {
float t = (float) (System.currentTimeMillis() - startTime) / duration;
t = t > 1.0f ? 1.0f : t;
float interpolatedRatio = interpolator.getInterpolation(t);
float tempScale = scale + interpolatedRatio * (targetScale - scale);
float tempX = x + interpolatedRatio * (finalX - x);
float tempY = y + interpolatedRatio * (finalY - y);
matrix.reset();
// translate initialPoint to 0,0 before applying zoom
matrix.postTranslate(-doubleTapImagePoint[0], -doubleTapImagePoint[1]);
// zoom
matrix.postScale(tempScale, tempScale);
// translate back to equivalent point
matrix.postTranslate(tempX, tempY);
imageView.setImageMatrix(matrix);
if (t < 1f) {
imageView.post(this);
}
}
});
return false;
}