I am working with andEngine the Open source game platform. I have a sprite that moves continuously on the screen and change direction when collides with screen boundary. Now, I wanna change its direction to the players touch point on the screen. I can't manage this part. I use PhysicsHandler to move the sprite with a velocity. I understand i have to implements IOnSceneTouchListener, to get touched point and set the direction on the sprite . But found nothing now. Here is my code goes:
Pilot aPilot;
PhysicsHandler mPhysicsHandler;
aPilot = new Pilot(222, 333, pilotTexures, vbom) {
#Override
protected void onManagedUpdate(float pSecondsElapsed) {
/*
* change direction when collides with boundary wall of Screen
*/
if (this.mX < 0) {
mPhysicsHandler.setVelocityX(AtomicEngine.DEMO_VELOCITY);
} else if (this.mX + this.getWidth() > ResourcesManager.CAMERA_WIDTH) {
mPhysicsHandler.setVelocityX(-AtomicEngine.DEMO_VELOCITY);
}
if (this.mY < 0) {
mPhysicsHandler.setVelocityY(AtomicEngine.DEMO_VELOCITY);
} else if (this.mY + this.getHeight() > ResourcesManager.CAMERA_HEIGHT) {
mPhysicsHandler.setVelocityY(-AtomicEngine.DEMO_VELOCITY);
}
super.onManagedUpdate(pSecondsElapsed);
}
};
/*
* initialize mPhysicsHandler
*/
mPhysicsHandler = new PhysicsHandler(aPilot);
registerUpdateHandler(this.mPhysicsHandler);
mPhysicsHandler.setVelocity(AtomicEngine.DEMO_VELOCITY,
AtomicEngine.DEMO_VELOCITY);
attachChild(aPilot);
aPilot.setScale(3f);
And my override onSceneTouchEvent method is like:
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
if (pSceneTouchEvent.isActionDown()) {
// need some idea here
}else if (pSceneTouchEvent.isActionMove()) {
}
return false;
}
Wait for your super knock.
you have to calculate difference between the pilots current position (e.g. by calling getSceneCenterCoordinates() of your pilots sprite you get the coordinates in the scene) and the position of the the touch event - with that difference in mind, you can calculate the angle (measured on the UnitCircle) or use a factor that is a percentage between your max_velocity & distance length, then use your physicshandler and set a new velocity. the factor is used to limit the speed to a max speed.
so, your code should look like something like this (didn't test, ask if it didn't work)
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
MainActivity.this.mCamera.convertCameraSceneToSceneTouchEvent(touchEvent);//see edit
float touchX = touchEvent.getX();
float touchY = touchEvent.getY();
float[] pilotCoord = aPilot.getEntity.getSceneCenterCoordinates();
float pilotX = pilotCoord[0];
float pilotY = pilotCoord[1];
float xDiff = touchX - pilotX;
float yDiff = touchY - pilotY; // could be wrong with AnchorCenter branch
// use the max velo divided by the distance to get the velo factor for x & y,
// but perhaps calculating angles is faster, dunno
float veloFactor = MAX_VELO/sqrt(xDiff^2 + yDiff^2);
float xVelo = xDiff*veloFactor;
float yVelo = yDiff*veloFactor;
mPhysicshandler.setVelocityX(xVelo);
mPhysicshandler.setVelocityY(yVelo);
return true;
}
so far the calculation for setting the velocity into the direction of the finger. if you want some kind of (de)acceleration (like: as long as the finger is down, the pilot (de)accelerates into the direction of the finger, else he will stick with his speed, you have to setLinearVelocity(xVelo, yVelo) instead and set the current velocity as velocity (to maintain speed)
Edit
The conversion of the touchEvent from a CameraScene to a SceneTouchEvent is only usefull if you add your onSceneTouchListener to your HUD. it converts the events x/y values based on the current camera position (over the scene) to xy-values as they would have occured on the scene.
else, if you add the listener directly to your Scene, you don't need to convert the touch event and the line could be deleted.
Related
I need help with the following problem since I have invested many days without arriving at an acceptable solution.
I'm doing an Android game (using libgdx) where the main character (named Hero) is seen from above (top-down view game) and walks on a field.
The user moves the character by moving his finger along the screen. The finger does't need to be on the character.
The character uses two animations, one animation when he moves forward (that is, when his "y" is greater than zero since the user looks at the game from the "sky") and another animation when he moves backwards (that is, when his "y" is less than zero, remember I'm developing a top-down view game).
Finally, I need the character to always move at a CONSTANT speed.
In short, I would like to handle the character with the finger and move it in the direction that marks my finger, always a CONSTANT speed.
This would be very easy if I could set the position of the character each delta time, but I'm using box2d which only knows about linearVelocity, impulses, forces, etc.
I tried using mouseJoint where hitbody is my main character (Hero) and groundBody is an invisible body.
// Invisible zero size ground body
// to which we can connect the mouse joint
Body groundBody;
BodyDef bodyDef = new BodyDef();
groundBody = world.createBody(bodyDef);
/* player is an instance of my Hero's class, which has a box2d body and
update, draw methods, etc.
*/
hitBody = player.getB2body();
...
InputProcessor:
#Override
public boolean touchDown(int i, int i1, int i2, int i3) {
gameCam.unproject(testPoint.set(i, i1, 0));
MouseJointDef def = new MouseJointDef();
def.bodyA = groundBody;
def.bodyB = hitBody;
def.collideConnected = true;
def.target.set(testPoint.x, testPoint.y);
def.maxForce = 1000.0f * hitBody.getMass();
mouseJoint = (MouseJoint) world.createJoint(def);
hitBody.setAwake(true);
}
#Override
public boolean touchUp(int i, int i1, int i2, int i3) {
player.getB2body().setLinearVelocity(0,0);
// if a mouse joint exists we simply destroy it
if (mouseJoint != null) {
world.destroyJoint(mouseJoint);
mouseJoint = null;
}
return false;
}
#Override
public boolean touchDragged(int i, int i1, int i2) {
// if a mouse joint exists we simply update
// the target of the joint based on the new
// mouse coordinates
if (mouseJoint != null) {
gameCam.unproject(testPoint.set(i, i1, 0));
mouseJoint.setTarget(target.set(testPoint.x, testPoint.y));
evaluateMovementDirection();
}
return false;
}
private void evaluateMovementDirection() {
float vy = player.getB2body().getLinearVelocity().y;
float vx = player.getB2body().getLinearVelocity().x;
// Test to Box2D for velocity on the y-axis.
// If Hero is going positive in y-axis he is moving forward.
// If Hero is going negative in y-axis he is moving backwards.
if (vy > 0.0f) {
player.onMovingUp(); // In draw, I'll use a "moving forward" animation
} else if (vy < 0.0f) {
player.onMovingDown(); // In draw, I'll use a "movieng backwards" animation
} else {
player.onStanding(); // vy == 0 In draw, I'll use a texture showing my Hero standig.
}
}
The problem I get with this, is that if I move my finger very fast, the character moves very fast. I would like the character to always move AT CONSTANT SPEED.
The other approach I tried is to use the pan event:
GestureListener:
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
/*
* DeltaX is positive when I move my finger to the left, negative otherwise.
* DeltaY is positive when I move my finger down, negative otherwise.
*/
// In b2body y-axes sign is the opposite.
deltaY = -deltaY;
// DeltaX and deltaY are in pixels, therefore delta is in metres.
Vector2 delta = new Vector2(deltaX / Constants.PPM, deltaY / Constants.PPM);
// Deltas too small are discarded
if (delta.len() > Constants.HERO_SENSIBILITY_METERS) {
/*
* origin.x = player.getB2body().getPosition().x
* origin.y = player.getB2body().getPosition().y
*
* destination.x = origin.x + delta.x
* destination.y = origin.y + delta.y
*
* To go from origin to destination we must subtract their position vectors: destination - origin.
* Thus destination - origin is (delta.x, delta.y).
*/
Vector2 newVelocity = new Vector2(delta.x, delta.y);
// Get the direction of the previous vector (normalization)
newVelocity.nor();
// Apply constant velocity on that direction
newVelocity.x = newVelocity.x * Constants.HERO_LINEAR_VELOCITY;
newVelocity.y = newVelocity.y * Constants.HERO_LINEAR_VELOCITY;
// To avoid shaking, we only consider the newVelocity if its direction is slightly different from the direction of the actual velocity.
// In order to determine the difference in both directions (actual and new) we calculate their angle.
if (Math.abs(player.getB2body().getLinearVelocity().angle() - newVelocity.angle()) > Constants.HERO_ANGLE_SENSIBILITY_DEGREES) {
// Apply the new velocity
player.getB2body().setLinearVelocity(newVelocity);
evaluateMovementDirection();
}
} else {
// Stop
player.getB2body().setLinearVelocity(0, 0);
evaluateMovementDirection();
}
return true;
}
The problem I have with this is that the movement is very unstable and "dirty". The character is shaking.
I tried this approach (thanks #hexafraction).
Using this code, my charecter moves more fluid along the screen.
It's not perfect, but it's something...
#Override
public boolean pan(float x, float y, float deltaX, float deltaY) {
/*
* DeltaX is positive when I move my finger to the left, negative otherwise.
* DeltaY is positive when I move my finger down, negative otherwise.
* Both are in pixels, thus to get meters I must divide by Constants.PPM.
*/
// In b2body y-axes sign is the opposite.
deltaY = -deltaY;
/*
* origin.x = player.getB2body().getPosition().x
* origin.y = player.getB2body().getPosition().y
*
* destination.x = origin.x + deltaX / Constants.PPM
* destination.y = origin.y + deltaY / Constants.PPM
*
* To go from origin to destination we must subtract their position vectors: destination - origin.
* Thus, destination - origin is (deltaX / Constants.PPM, deltaY / Constants.PPM).
*/
candidateVelocity.x = deltaX / Constants.PPM;
candidateVelocity.y = deltaY / Constants.PPM;
// Get the direction of the previous vector (normalization)
candidateVelocity.nor();
// Apply constant velocity on that direction
candidateVelocity.x = candidateVelocity.x * Constants.HERO_LINEAR_VELOCITY;
candidateVelocity.y = candidateVelocity.y * Constants.HERO_LINEAR_VELOCITY;
// Linear interpolation to avoid character shaking
heroVelocity.lerp(candidateVelocity, Constants.HERO_ALPHA_LERP);
// Apply the result
player.getB2body().setLinearVelocity(heroVelocity);
// Depending on the result, we change the animation if needed
evaluateMovementDirection();
}
return true;
}
I need a suggestion on how to resolve this. I mean, move a box2d character with my finger along the screen at CONSTANT SPEED.
Thank you very much.
Calculate the direction in which you want to move:
dragPos.sub(currentPos);
Normalize it and multiply with the constant speed:
dragPos.sub(currentPos).nor().scl(CONSTANT_SPEED);
I have an Android application that uses Native OpenCV Library to track objects in my camera view. I find the position of the objects using moments:
Moments moment = moments((Mat) contours[i]);
double area = moment.m00;
object.setXPos(moment.m10 / area);
object.setYPos(moment.m01 / area);
What I am trying to implement is a way to see if my finger touch Point is within a distance threshold to the object position. However, Android calculates my finger position based on touch location on screen, whereas the object's position is calculated by moments, which I believe is causing wacky results when I calculate the distance from the touch event to the object location. Is there any way to remedy this, or am I going about this the wrong way? Thanks in advance for your help!
Other possibly useful info:
#Override
public boolean onTouchEvent(MotionEvent event) {
double x = event.getX();
double y = event.getY();
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
{
//jni function. Converts x and y to Point(x, y) and compares its distance to tracked object locations
GetTouchedPoint(x, y);
}
}
return false;
}
//Native function: p1 is finger touch location, p2 is object location (found by moments)
int ObjectDetector::distance(Point p1, Point p2) {
int dx = p1.x - p2.x;
int dy = p1.y - p2.y;
int distance = sqrt(pow(dx, 2) + pow(dy, 2));
return distance;
}
I am looking for a way to connect pan gesture with percentage of animation completion. Let me show you what I mean.
This image represents an animation that I want to execute, namely a moving Image actor or a sprite. The animation gets executed by pan gesture. Animation is 100% complete and at stage 6 when user slides for a 200px. If user slided only 100px, it would be 50% complete and at stage 3. If the user didn't execute pan gesture the animation stays at 0% and at stage 1. I am looking for tips on how to start building such a model. I believe it is called interactive. Do you have any suggestions?
You can use a GestureDetector to handle the panning input. The gestureListener.pan method can update an animation position parameter.
private int screenPixelsToAnimationPositionRatio = 0.01f; //will need to tweak this and probably adjust it at runtime based on screen dimensions and resolution
private float animationPosition = 0; //from 0 to 1, fraction of animation complete
public void create () {
//...
GestureAdapter gestureAdapter = new GestureAdapter {
#Override
public boolean pan (float x, float y, float deltaX, float deltaY) {
animationPosition += deltaX * screenPixelsToAnimationPositionRatio;
animationPosition = MathUtils.clamp(animationPosition, 0, 1);
return true;
}
};
GestureDetector gestureDetector = new GestureDetector(gestureAdapter);
Gdx.input.setInputProcessor(gestureDetector); //or put the detector in an InputMultiplexer with your other input listeners.
}
Then you would create a method that can update your object's position and rotation based on the current value of animationPosition. You would need to figure out the equations that determine the movement you want. For example, something that looks sort of like what you illustrated above:
private void updateAnimation (){
x = animationPosition * 30;
float y = 0, rotation = 0;
if (animationPosition >= 0.25f) {
float jumpPosition = Math.min(1, (animationPosition - 0.25f) / 0.5f);
y = 30 * (1 - Interpolation.pow2In.apply(Math.abs(2 * jumpPosition - 1)));
rotation = 180 * jumpPosition;
}
mySprite.setPosition(x, y);
mySprite.setRotation(rotation);
}
Then call this update method somewhere in render.
Is it Possible to move and rotate an Image along a Circular Path based on a touch event as follows:
I have looked at this question:
Moving an Image in circular motion based on touch events in android
But it only tells me how to move the image along a circle, not rotate it.
Update: Full example posted on GitHub at https://github.com/jselbie/xkcdclock
Every time you get a touch event, grab the touch point's x,y coordinates and compute the angle of the rotation relative to the center of bitmap. Use that value to determine how much to rotate the bitmap you want draw.
First, let's assume a logical coordinate system in which the center point of your element above is at (0,0) in x,y space.
Therefore, the angle (in degrees) between any touch point relative to the center can be computed as follows:
double ComputeAngle(float x, float y)
{
final double RADS_TO_DEGREES = 360 / (java.lang.Math.PI*2);
double result = java.lang.Math.atan2(y,x) * RADS_TO_DEGREES;
if (result < 0)
{
result = 360 + result;
}
return result;
}
Note - the normalization of negative angles to positive angles. So if the touch point is (20,20), this function above will return 45 degrees.
To make use of this method, your Activity will need the following member variables defined:
float _refX; // x coordinate of last touch event
float _refY; // y coordinate or last touch event
float _rotation; // what angle should the source image be rotated at
float _centerX; // the actual center coordinate of the canvas we are drawing on
float _centerY; // the actual center coordinate of the canvas we are drawing on
Now let's examine how to keep track of touch coordinates to we can always have an up to date "_rotation" variable.
So our "touch handler" for Android will look something like this:
boolean onTouch(View v, MotionEvent event)
{
int action = event.getAction();
int actionmasked = event.getActionMasked();
if (!_initialized)
{
// if we haven't computed _centerX and _centerY yet, just bail
return false;
}
if (actionmasked == MotionEvent.ACTION_DOWN)
{
_refX = event.getX();
_refY = event.getY();
return true;
}
else if (actionmasked == MotionEvent.ACTION_MOVE)
{
// normalize our touch event's X and Y coordinates to be relative to the center coordinate
float x = event.getX() - _centerX;
float y = _centerY - event.getY();
if ((x != 0) && (y != 0))
{
double angleB = ComputeAngle(x, y);
x = _refX - _centerX;
y = _centerY - _refY;
double angleA = ComputeAngle(x,y);
_rotation += (float)(angleA - angleB);
this.invalidate(); // tell the view to redraw itself
}
}
There's some fine details left out such as drawing the actual bitmap. You might also want to handle the ACTION_UP and ACTION_CANCEL events to normalize _rotation to always be between 0 and 360. But the main point is that the above code is a framework for computing the _rotation at which your Bitmap should be drawn on the View. Something like the following:
void DrawBitmapInCenter(Bitmap bmp, float scale, float rotation, Canvas canvas)
{
canvas.save();
canvas.translate(canvas.getWidth()/2, canvas.getHeight()/2);
canvas.scale(scale, scale);
canvas.rotate(rotation);
canvas.translate(-bmp.getWidth()/2, -bmp.getHeight()/2);
canvas.drawBitmap(bmp, 0, 0, _paint);
canvas.restore();
}
I have a background image as a drawable in my custom view. This drawable may be pinch zoomed or moved.
Currently I need a green dot that is drawn on the image to be stationary relative to the screen. That is, it should be always at the same position with the pin as shown below. (Of course, the pin is simply an ImageView and does NOT move at all!)
I have successfully made it stationary relative to the screen, when the map behind is moved as follows in my custom view, MapView:
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: { // triggered as long as finger movers
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
// update the starting point if the 'Start' button is not yet pressed
// to ensure the screen center (i.e. the pin) is always the starting point
if (!isStarted) {
Constant.setInitialX(Constant.INITIAL_X - dx);
Constant.setInitialY(Constant.INITIAL_Y - dy);
if ((historyXSeries.size() > 0) && (historyYSeries.size() > 0)) {
// new initial starting point
historyXSeries.set(0, Constant.INITIAL_X);
historyYSeries.set(0, Constant.INITIAL_Y);
}
}
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
By doing that above, my green dot stays there, when the background image is moved.
But I have problems in trying to make it stay there, when the background image is zoomed.
Essentially, I don't really understand how canvas.scale(mScaleFactor, mScaleFactor) works, and therefore I cannot move the green dot accordingly like what I have done in the simple moving case.
I think something should be added in the scale listener handler below, could anybody help me fill that part?
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 10.0f)); // 1 ~ 10
// HOW TO MOVE THE GREEN DOT HERE??
invalidate();
return true;
}
Or please at least explain how canvas.scale(mScaleFactor, mScaleFactor) works, and how may I move the green dot accordingly?
Keep in mind that the canvas is thought to scale everything according to the scale factor, so while going against the zoom is possible, it is probably not the best approach. However, if this is what you're looking for, I will help you as best as I can.
I am assuming the following:
Scale factor is relative to the current zoom (old zoom is always scale factor 1). If this is not the case, then you should observe the zoom values after scaling roughly 200% two times and seeing if the resulting scale factor is 4 or 3 (exponential or linear). You can achieve the results below by normalizing the scale factor to 2 for a zoom factor of 200%, for example. You'll have to remember the old scale factor in order to do so.
No rotation is performed
If this is the case then following can be said for a marker with respect to the zoom center.
For every horizonal pixel x away from the zoom center after zoom, its original position could be calculated to be: zoom_center_x + *x* / scale_factor (or alternatively zoom_center_x + (marker_x - zoom_center_x) / scale_factor). In other words, if zoom center is (50, 0) and the marker is (100, 0) with a scale factor of 2, then the x position of the marker prior to the zoom was 50 + (100 - 50) / 2 or 75. Obviously, if the marker is in the same position of the zoom center, then the x position will be the same as the zoom center. Similarly, if the scale is 1, then the x position for the marker will be the same as it is now.
The same can be applied to the y axis.
While I can't know exactly how to set the position of your marker, I would expect the code to look something like:
Point zoomCenter = detector.getZoomCenter();
// Set marker variable here
marker.setX(Math.round(zoomCenter.getX() + ((double)(marker.getX() - zoomCenter.getX())) / mScaleFactor));
marker.setY(Math.round(zoomCenter.getY() + ((double)(marker.getY() - zoomCenter.getY())) / mScaleFactor));
I hope that helps.