I have an image and its in imageView. I want to add a functionally that allows users to spin the image if they try to spin it to the right side.For example if user tries to spin it to the left side, it doesnt spin.
This is my spinwheel.
I tried to use GestureDetector and onFling event but it wasn't enough for me to detect if user tries to spin it to the right side or left side. How can i do this ?
Edit:
var normalVectorX = e2?.x!! - 515
var normalVectorY = e2?.y!! - 515
var tangentVectorX = -normalVectorY
var tangentVectorY = normalVectorX
var tangentVectorLength = (Math.sqrt(Math.pow((515 - e2?.y!!).toDouble(), 2.0)) + Math.pow((e2?.x!! - 515).toDouble(), 2.0))
var unitTangentX = tangentVectorX / tangentVectorLength
var unitTangentY = tangentVectorY / tangentVectorLength
var scalarProjection = (velocityX * unitTangentX) + (velocityY * unitTangentY)
if (scalarProjection > 0) // Right Side
spinWheel((spinCountX * 360).toFloat() + 360 - (mPrizeIndex * 60) , 12000)
This is the code implementation based on the pseudo code of answer.
515 = center of the wheel.
onFling gives you the MotionEvents of the corresponding fling gesture. You can then call getX(int) and getY(int) to get the coordinates.
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
float x = e2.getX(0); // Assuming you only care about the first finger
float y = e2.getY(0);
if (x < imageView.getWidth() / 2) {
// fling is on the left side
} else {
// fling is on the right side
}
}
Side note: I think the code above will behave unnaturally if the fling is horizontal. If you want your spin gesture to be more realistic, I think you will be better off calculating the tangential component.
Edit: Here's how you would calculate the tangential component.
You would want to calculate the scalar projection of the velocity vector on the unit tangent of the circle at the point where the touch event is.
Pseudo code:
velocityVector = (velocityX, velocityY)
locationOfTouch = (e2.getX(0), e2.getY(0))
centerOfWheel = (166, 155) # Measured from your image
normalVector = locationOfTouch - centerOfWheel
= (locationOfTouch.x - centerOfWheel.x, locationOfTouch.y - centerOfWheel.y)
To get the clockwise tangent vector, we take the normalVector, swap the X and Y components, and negate the X (from 2D Euclidean vector rotations).
tangentVector = (-normalVector.y, normalVector.x)
unitTangent = tangentVector / tangentVector.length()
Finally, you take the dot product to get the scalar projection:
scalarProjection = velocityVector.dotProduct(unitTangent)
= (velocityVector.x * unitTangent.x) + (velocityVector.y * unitTangent.y)
If scalarProjection is positive, that means the fling gesture is in the clockwise direction. If it is negative, that means the gesture is in anti-clockwise direction.
As a bonus, since we used the unit tangent, the resulting scalar projection also represents how fast the fling is in the clockwise direction, so you can change the spin animation to be faster or slower, or set a threshold such that a small velocity in that direction doesn't trigger the wheel to spin.
Related
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.
Hey I am using this code to imitate a knob view in my app:
- (void) setupGestureRecognizer
{
CGPoint midPoint = CGPointMake(image.frame.origin.x + image.frame.size.width / 2,
image.frame.origin.y + image.frame.size.height / 2);
CGFloat outRadius = image.frame.size.width / 2;
gestureRecognizer = [[OneFingerRotationGestureRecognizer alloc] initWithMidPoint: midPoint
innerRadius: outRadius / 10
outerRadius: outRadius *2
target: self];
[self.view addGestureRecognizer: gestureRecognizer];
}
Like this, the gestureRecognizer handles all the events that happen on or very close to the button. What I want is the following:
gestureRecognizer only gets triggered when user touches inside
the image
if finger leaves the image, gestureRecognizer should continue
listening (and calculating the angle)
On Android I am doing it like the following:
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
float x = e2.getX() / ((float) getWidth());
float y = e2.getY() / ((float) getHeight());
float rotDegrees = cartesianToPolar(1 - x, 1 - y);
[...doing maths stuff here]
I got all the rotating stuff working but how do you make the gestureRecognizer work like you can handle events in Android? If I lost my internet connection and had no choice but to code it completely on my own I would just take 2 different gestureRecognizers, on handling the "init" press and one "following" the finger everywhere, setting the correspondent knob according to the key value being set in the first gestureRecognizer. But this looks like a massive pile of bad code to me so I'd appreciate some advice on this one.
Cheers, Alex
You should be able to use the delegate method, gestureRecognizer:shouldReceiveTouch: to examine the location of the touch, and return YES only if the touch point is within the image view's bounds (using CGRectContainsPoint). If the gesture recognizer is added to the image view's superview, it should continue to "listening" like you want.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint touchPoint = [touch locationInView:self.view];
return CGRectContainsPoint(self.imageView.frame, touchPoint);
}
Also be sure to set the controller as the delegate of the gesture recognizer.
After a long hiatus, I've come back to a project I was working on last year which involves trying to scroll around a grid. I achieve this by handling the ACTION_MOVE case in the onTouchEvent of a custom class extending Canvas, and it all works very smoothly.
My problem is that I only want the user to be able to scroll the canvas so far, and after some boundaries have been reached to stop the scrolling. So I tried this:
case MotionEvent.ACTION_MOVE:
pointerIndex = event.findPointerIndex(activePointerId);
pointer.set(event.getX(pointerIndex),event.getY(pointerIndex));
//compute how far the finger slid
float dx = pointer.x - start.x;
float dy = pointer.y - start.y;
//adjust displacement values so that we don't go beyond the bounds
//determined by the (transformed) corners
matrix.mapPoints(transformedCorners,originalCorners);
//horizontal distance between left corners
float maxHorDisp = originalCorners[0] - transformedCorners[0];
//horizontal distance between right corners
float minHorDisp = originalCorners[2] - transformedCorners[2];
//vertical distance between top corners
float maxVertDisp = originalCorners[1] - transformedCorners[1];
//vertical distance between bottom corners
float minVertDisp = originalCorners[3] - transformedCorners[3];
dx = Math.max(minHorDisp,Math.min(maxHorDisp,dx);
dy = Math.max(minVertDisp, Math.min(maxVertDisp,dy);
//Only move if finger slides far away enough from the starting point
distance = Math.sqrt(dx*dx + dy*dy);
if((mode == DRAG) && (distance > 25)){
dspl.set(dx, dy);
matrix.set(savedMatrix);
matrix.postTranslate(dspl.x, dspl.y);
invalidate();
}
Here, originalCorners is just a float array keeping track of the corners of the screen before any transformations have occurred and is initialized like this:
viewWidth = w;
viewHeight = h;
originalCorners[0] = 0;
originalCorners[1] = 0;
originalCorners[2] = w;
originalCorners[3] = h;
and transformedCorners is another float array keeping track of where the corners are after any translations or zooming has occurred. It is always updated as follows:
matrix.mapPoints(transformedCorners,originalCorners);
This sort of achieves my goal of stopping scrolling past the boundaries, but the grid starts to jitter like crazy when sliding too far in any direction (before the boundaries are reached). You are initially stopped too early, but if you keep sliding in the same direction you can eventually get the edge of the grid to coincide with the edge of the screen.
I've tried a few things (like adding a little buffer value to xMax, yMax and subtracting it from xMin,yMin) but nothing has worked.
So how do I get rid of the jitter and also make it so you can get the edge of the grid to coincide with the edge of the screen with one smooth swipe?
Thanks very much!
here's my code:
float x;
#Override
public void onSensorChanged(SensorEvent event) {
if(event.values[0] > .2){
x = -1*(Math.round(event.values[0]));
centerx = centerx +(x*10);
}
else if(event.values[0] < -.2){
x = -1*Math.round(event.values[0]);
centerx = centerx +(x*10);
}
mCustomDrawableView.invalidate();
y = Math.round(event.values[1]);
if(event.values[2] > .4){
z = event.values[2];
}
}
I'm using the accelerometer to move a vertical line back and forth. centerx is the top and bottom point of the vertical line. I'm trying to make it so if I move my device to the right, the line will move to the left and look like it's still in the same position, as if it were in the physical world (think of augmented reality).
I'm using the values given out of the SensorEvent to do some arithmetic to move center line. The only problem is that the accelerometer values will go up when I move the advice to something like 5, but then decrease to 4 to 3 to 2 to 1 to 0 as I stop moving. So, the line just kind of bounces to the side and then returns to the original center. I'm looking for a way to get rid of this "bounce," and have the line stay in it's new position. Any ideas?
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.