LibGDX: Scale Movement Projection Line to Always be the Same Length - debugging

I'm working on a game in LibGDX. Right now, I am working on drawing a line from a moving entity's body current position in the direction that it is moving. Maybe I didn't word that correctly, so here's my very artistic representation of what I'm talking about.
The problem that I'm having is that vertical lines are always much longer than diagonal lines, and diagonal lines are always much longer than horizontal lines. What I'm wanting is for the line being projected from the entity to always be the same length regardless of the direction.
Below is the code used for drawing lines from the center of an entity's body. As you can see, I am scaling the line (e.g., by 25.0f). Maybe there's a formula that I could use to dynamically change this scalar depending on the direction?
public class BodyMovementProjection implements Updatable {
public final Body body;
public final ShapeRenderer shapeRenderer;
public boolean debugProjection = false;
public float scalar = 25.0f;
private final Vector2 posThisFrame = new Vector2();
private final Vector2 posLastFrame = new Vector2();
private final Vector2 projection = new Vector2();
private float[] debugColorVals = new float[4];
public BodyMovementProjection(Body body) {
this.body = body;
this.shapeRenderer = body.entity.gameScreen.shapeRenderer;
} // BodyMovementProjection constructor
#Override
public void update() {
body.aabb.getCenter(posThisFrame);
posLastFrame.set(posThisFrame).sub(body.bodyMovementTracker.getSpritePosDelta());
projection.set(posThisFrame).sub(posLastFrame).scl(scalar).add(posLastFrame);
if (debugProjection) {
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
shapeRenderer.setColor(debugColorVals[0], debugColorVals[1], debugColorVals[2], debugColorVals[3]);
shapeRenderer.line(posLastFrame, projection);
shapeRenderer.end();
} // if
} // update
public void setDebugColorVals(float r, float g, float b, float a) {
debugColorVals[0] = r;
debugColorVals[1] = g;
debugColorVals[2] = b;
debugColorVals[3] = a;
} // setDebugColorVals
} // BodyMovementProjection

Normalize before you scale.
By normalizing you are making the directional vector 1 unit in length, and by scaling it after by 25 you get a vector that is 25 units every time, regardless of how far apart thw current and previous positions are.

Related

Android - How to make google Maps display a polyline that animates sequenctial flashing dots

I am searching for a way to animate the dots between two markers on a google map in android device.
So what i want in the end is the following line between the two images:
and it would be used like this typical google polyline implementation:
lets say there is a point A and a Point B. if im directing the user to point B, then the line animates to from point A to point B so the user knows to walk in this direction.
to achieve this i thought i could get the points out of the polyLine and remove them and add them back
rapidily. so lets say i had 5 points in the polyLine, i would remove position 1 , then put it back, then remove position 2, and put it back, to simulate this animation.
but it does not work . once hte polyline is set it seems i cannot alter it. you have any suggestions ?
val dotPattern = Arrays.asList(Dot(), Gap(convertDpToPixel(7).toFloat()))
val polyLineOptions: PolylineOptions = PolylineOptions()
.add(usersLocation)
.add(users_destination)
.pattern(dotPattern)
.width(convertDpToPixel(6).toFloat())
dottedPolyLine = googleMap.addPolyline(polyLineOptions)
dottedPolyLine?.points?.removeAt(1) // here as a test if my idea i try removing a point but it looks like a point here means current location or destination so there always 2. i thought a point would be one of the dots.
You can use MapView-based custom view View Canvas animationlike in this answer:
This approach requires
MapView-based
custom
view,
that implements:
drawing over the MapView canvas;
customizing line styles (circles instead of a simple line);
binding path to Lat/Lon coordinates of map
performing animation.
Drawing over the MapView needs to override dispatchDraw().
Customizing line styles needs
setPathEffect()
method of
Paint
class that allows to create create path for "circle stamp" (in
pixels), which will repeated every "advance" (in pixels too),
something like that:
mCircleStampPath = new Path(); mCircleStampPath.addCircle(0,0,
CIRCLE_RADIUS, Path.Direction.CCW); mCircleStampPath.close();
For binding path on screen to Lat/Lon coordinates
Projection.toScreenLocation()
needed, that requires
GoogleMap
object so custom view should implements OnMapReadyCallback for
receive it. For continuous animation
postInvalidateDelayed()
can be used.
but not draw path directly from point A to point B, but from point A to point C that animated from A to B. To get current position of point C you can use SphericalUtil.interpolate() from Google Maps Android API Utility Library. Something like that:
public class EnhancedMapView extends MapView implements OnMapReadyCallback {
private static final float CIRCLE_RADIUS = 10;
private static final float CIRCLE_ADVANCE = 3.5f * CIRCLE_RADIUS; // spacing between each circle stamp
private static final int FRAMES_PER_SECOND = 30;
private static final int ANIMATION_DURATION = 10000;
private OnMapReadyCallback mMapReadyCallback;
private GoogleMap mGoogleMap;
private LatLng mPointA;
private LatLng mPointB;
private LatLng mPointC;
private float mCirclePhase = 0; // amount to offset before the first circle is stamped
private Path mCircleStampPath;
private Paint mPaintLine;
private final Path mPathFromAtoC = new Path();
private long mStartTime;
private long mElapsedTime;
public EnhancedMapView(#NonNull Context context) {
super(context);
init();
}
public EnhancedMapView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public EnhancedMapView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public EnhancedMapView(#NonNull Context context, #Nullable GoogleMapOptions options) {
super(context, options);
init();
}
#Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.save();
drawLineFomAtoB(canvas);
canvas.restore();
// perform one shot animation
mElapsedTime = System.currentTimeMillis() - mStartTime;
if (mElapsedTime < ANIMATION_DURATION) {
postInvalidateDelayed(1000 / FRAMES_PER_SECOND);
}
}
private void drawLineFomAtoB(Canvas canvas) {
if (mGoogleMap == null || mPointA == null || mPointB == null) {
return;
}
// interpolate current position
mPointC = SphericalUtil.interpolate(mPointA, mPointB, (float) mElapsedTime / (float)ANIMATION_DURATION);
final Projection mapProjection = mGoogleMap.getProjection();
final Point pointA = mapProjection.toScreenLocation(mPointA);
final Point pointC = mapProjection.toScreenLocation(mPointC);
mPathFromAtoC.rewind();
mPathFromAtoC.moveTo(pointC.x, pointC.y);
mPathFromAtoC.lineTo(pointA.x, pointA.y);
// change phase for circles shift
mCirclePhase = (mCirclePhase < CIRCLE_ADVANCE)
? mCirclePhase + 1.0f
: 0;
mPaintLine.setPathEffect(new PathDashPathEffect(mCircleStampPath, CIRCLE_ADVANCE, mCirclePhase, PathDashPathEffect.Style.ROTATE));
canvas.drawPath(mPathFromAtoC, mPaintLine);
}
private void init() {
setWillNotDraw(false);
mCircleStampPath = new Path();
mCircleStampPath.addCircle(0,0, CIRCLE_RADIUS, Path.Direction.CCW);
mCircleStampPath.close();
mPaintLine = new Paint();
mPaintLine.setColor(Color.BLACK);
mPaintLine.setStrokeWidth(1);
mPaintLine.setStyle(Paint.Style.STROKE);
mPaintLine.setPathEffect(new PathDashPathEffect(mCircleStampPath, CIRCLE_ADVANCE, mCirclePhase, PathDashPathEffect.Style.ROTATE));
// start animation
mStartTime = System.currentTimeMillis();
postInvalidate();
}
#Override
public void getMapAsync(OnMapReadyCallback callback) {
mMapReadyCallback = callback;
super.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
mGoogleMap = googleMap;
mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
#Override
public void onCameraMove() {
invalidate();
}
});
if (mMapReadyCallback != null) {
mMapReadyCallback.onMapReady(googleMap);
}
}
public void setPoints(LatLng pointA, LatLng pointB) {
mPointA = pointA;
mPointB = pointB;
}
}
NB! This is just idea, not full tested code.

JavaFX: Mapping pixels on screen to a 2D matrix

I'm making an animation in which I need to keep track of explored/unexplored pixels on the screen.Initially the screen is black colored,then as the node(a circle) moves(over defined path) the explored pixels are set to white.For doing this task(color change) I'm using Canvas class of JavaFX as background and painting the path using an object of GraphicsContext class(see the createPathAnimation method),now I want to update the int 2D matrix as 0-unexplored,1-explored.
How can I use the changed() function inside createPathAnimation to update my matrix as that function is updating the pixel color to white and I need to update the same set of explored pixels to 1 in my matrix?
sample translation
I'm trying to use the inbuilt function because even if I know the initial and final pixel coordinates,its not easy to determine which all pixels will be set while the circle moves between them(for ex along one of the diagonals),since circle is a like a blob of tiny squares on a pixel level.
My motive is to find the number of white colored pixels after a diagonal translation.
public void start(Stage primaryStage)throws Exception{
Pane root=new Pane();
Path path1=createPath();
canvas=new Canvas(800,600);
root.getChildren().addAll(path1,canvas);
primaryStage.setScene(new Scene(root,800,600,Color.BLACK));
primaryStage.show();
Animation animation1=createPathAnimation(path1,Duration.seconds(10));
pt.getChildren().addAll(animation1);
pt.play();
}
private Path createPath(){
Path path=new Path();
path.setStroke(Color.BLACK);
path.setStrokeWidth(10);
path.getElements().add(new MoveTo(400,300));
path.getElements().add(new LineTo(600,500));
return path;
}
public int a,b;
private Animation createPathAnimation(Path path,Duration duration){
GraphicsContext gc=canvas.getGraphicsContext2D();
Circle pen=new Circle(0,0,10);
PathTransition pathTransition=new PathTransition(duration,path,pen);
pathTransition.currentTimeProperty().addListener(new ChangeListener<Duration>(){
Location oldLocation = null;
/**
* Draw a line from the old location to the new location
*/
#Override
public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
if( oldValue == Duration.ZERO)
return;
// get current location
double x = pen.getTranslateX();
double y = pen.getTranslateY();
// initialize the location
if( oldLocation == null) {
oldLocation = new Location();
oldLocation.x = x;
oldLocation.y = y;
return;
}
// draw line
gc.setStroke(Color.WHITE);
gc.setLineWidth(30);
gc.strokeLine(oldLocation.x, oldLocation.y, x, y);
// update old location with current one
oldLocation.x = x;
oldLocation.y = y;
}
});
return pathTransition;
}
public static class Location {
double x;
double y;

Unity: How to get y axis in run time generated zigzag path

I am create a runner game like Hilclimb, But i don't know how to generated coin in the surface of zigzag path.
This is my Path
it is generated in random. But how to get y position in path in run time, because when coin generated in random position then coin is some time below the and some time above the path.
So please help me how to get path y position in run time so generate coin in the surface.
Path generate code is this, and coin also.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class TerrainGenerator : MonoBehaviour {
public chunk previousChunk;
public chunk currentChunk;
public chunk nextChunk;
public ManagePower power;
public GameObject character;
public Resize resize;
public Coin coin;
private PickupResize rise;
private pickupPower pick;
private PickupCoin pCoin;
// Use this for initialization
void Start () {
rise=FindObjectOfType<PickupResize>();
pick=FindObjectOfType<pickupPower>();
pCoin=FindObjectOfType<PickupCoin>();
float widthprefabs = previousChunk.WidthGroundPrefab;
previousChunk.RepositionChunk (new Vector3(-widthprefabs*chunk.CHUNK_SIZE,0,0));
currentChunk.RepositionChunk (Vector3.zero);
nextChunk.RepositionChunk (new Vector3(widthprefabs*chunk.CHUNK_SIZE,0,0));
power.RepositionPower (new Vector3(widthprefabs*chunk.CHUNK_SIZE,-0.1f,0));
resize.ResizeSquire (new Vector3(25f,-0.1f,0));
coin.ResetCoin (new Vector3(5f,-0.17f,0));
}
// Update is called once per frame
void Update () {
if(playerReachedNextChunk()){
reassineChunk();
}
if (rise.rePick) {
resize.ResizeSquire(new Vector3 (rise.distancere, -0.1f, 0));
rise.rePick=false;
}
if (pick.poPick) {
power.RepositionPower (new Vector3 (pick.powerdist, -0.1f, 0));
pick.poPick=false;
}
if (pCoin.distancere < ObjectScript.distance) {
int random = Random.Range (1, 10);
pCoin.distancere += random;
pCoin.reCoin = true;
}
if (pCoin.reCoin) {
coin.ResetCoin (new Vector3 (pCoin.distancere, -0.1f, 0));
pCoin.reCoin=false;
}
//print ("ddhdhdhdhhd"+ObjectScript.distance);
}
private bool playerReachedNextChunk(){
bool playerReachedNextChunk = false;
float distance = character.transform.position.x - nextChunk.transform.position.x;
//print ("Distance " +distance);
if (distance > 0) {
playerReachedNextChunk=true;
}
return playerReachedNextChunk;
}
private void reassineChunk(){
chunk refToprivesChunk = previousChunk;
previousChunk = currentChunk;
currentChunk = nextChunk;
nextChunk = refToprivesChunk;
float xPosition = nextChunk.transform.position.x+3 * nextChunk.WidthGroundPrefab * chunk.CHUNK_SIZE;
nextChunk.RepositionChunk (new Vector3(xPosition,0,0));
}
}
If you do not have access to exact points on your path, but you are using a collider, that approximates the path, then you can use a Physics.Raycast or Physics2D.Raycast to determine the y-coordinate of the path given x (and if you are in 3D space z).
Given x (assuming you are in 2D space) you could proceed as follows:
1. Choose a value for y that is guaranteed to be below/above your path
2. If you select a value for y that is above, then perform a Physics.Raycast starting from (x,y) in direction Vector2.down. You can also utilize layer masks to make sure, you only hit the collider of your path.
3. The y-component of the hit point is the value for y of the path you are looking for

Converting from Matrix4 to Vec3,Quaternion,Vec3 and back again

Libgdx uses a Matrix4 to express the world space position, rotation and scaleing of objects.
As I am animating these objects a lot in my code I wish to convert them to a format that keeps Position, Rotation and Scale Separate.
This makes it easier to interpolate correctly in order to animate them.
I am storing the Scale and Position both as Vector3s.
And the rotation as a Quaternion.
Here is my class that stores these 3 and converts two and from a Matrix4;
public class PosRotScale {
private static String logstag="ME.PosRotScale";
Vector3 position = new Vector3();
Quaternion rotation = new Quaternion();
Vector3 scale = new Vector3(1f,1f,1f);
public PosRotScale(Vector3 position, Quaternion rotation, Vector3 scale) {
super();
this.position = position;
this.rotation = rotation;
this.scale = scale;
}
public PosRotScale(Matrix4 setToThis) {
this.setToMatrix(setToThis);
}
#Override
public String toString(){
Vector3 axis = new Vector3();
float angle = rotation.getAxisAngle(axis);
String stateAsString = "["+position.x+","+position.y+","+position.z+"]"
+ "["+axis.x+","+axis.y+","+axis.z+","+angle+"]"
+ "["+scale.x+","+scale.y+","+scale.z+"]";
return stateAsString;
}
public void setToMatrix(Matrix4 lastLocation) {
lastLocation.getTranslation(position);
lastLocation.getRotation(rotation);
lastLocation.getScale(scale);
}
public PosRotScale setToRotation(float i, float j, float k, float angleInDeg) {
rotation.set(new Vector3(i,j,k), angleInDeg);
return this;
}
public PosRotScale setToPosition(Vector3 newposition) {
position = newposition.cpy();
return this;
}
public PosRotScale setToScaling(Vector3 newscale) {
scale = newscale.cpy();
return this;
}
public Matrix4 createMatrix() {
return new Matrix4(position,rotation.nor(),scale);
}
}
(I apologise for the snippet above having a "run as" and being interpreted as javascript - I didn't see a option in the code snippet box for Java)
Anyway, in order to test if this was working I created a test state and converted to and from it.
Unfortunately the values clearly didn't match.
I expected the rotation to be off a bit (seems to be the nature of rotations) but scale is clearly wayyyy of. Any pointers as to what I am doing wrong?
Test code;
PosRotScale startScaleAndRotation = new PosRotScale();
startScaleAndRotation.setToPosition(new Vector3(30f, 40f, 50f));
startScaleAndRotation.setToRotation(0f, 0f, 1f, 45);
startScaleAndRotation.setToScaling(new Vector3(0.5f, 2.5f,0.5f));
Gdx.app.log(logstag, " setting to: "+startScaleAndRotation.toString());
Matrix4 test = startScaleAndRotation.createMatrix();
PosRotScale test2 = new PosRotScale(test);
Gdx.app.log(logstag, " check after conversion: "+test2.toString());
Results in:
setting to: [30.0,40.0,50.0][0.0,0.0,0.99999994,45.000004][0.5,2.5,0.5]
check after conversion: [30.0,40.0,50.0][0.0,0.0,1.811493,35.83953][1.8027757,1.8027754,0.5]

How to control an animation by touch position along a path in Unity3D?

I have a GameObject that I want to animate along a specific path/curve, but the animation should be controlled by mouse/touch position. So when I touch/click on the GameObject and move the finger/mouse on/near the path (or maybe its easier to just move down) the GameObject should follow its defined path.
I like iTween, but I think it is not possible to find a solution using it here, right?
edit: added image:
It's quite a simpler task than what you might think.
Basically it's a question of remapping a function (that takes the input as parameter) to another function (that express a position along a path).
There are several ways of doing that, depending on the precise effect you want to implement.
The most important choices you have to take are:
How the describe the path/curve
How to handle input
Example
For the path an easy and flexible way is to use some sort of spline curves, such as cubic Bézier curve. It's easy to implement and Unity3D provides built-in functions to draw them. Have a look at Handles.DrawBezier.
Basically a Bézier function takes as input a parameter t in the domain [0,1] and return as a result a point in the space (2D or 3D as you prefer). B(0) gives the point at the begin of the curve, B(1) the end point. (Side note: the function is not linear so in the general case incrementing at a constant rate t doesn't produce a movement at constant speed along the curve. This paper might be useful).
For what concern the input the simpler solution that comes up to my mind is the following:
Accumulate somewhere the vector describing the offset from the position when the touch started to the current touch position. (Here's how to handle touches, have a look at deltaPosition).
Something like:
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Moved)
{
offsetFromStartPos += Input.GetTouch(0).deltaPosition;
}
Let's say you want to swipe up/down your finger for moving forward/back an object along a path.Choose a "travel" distance (the domain of the input function) for your finger in order to complete the movement along the curve and normalize the offset using such distance in order to remap the input into the [0,1] domain.
float t = offsetFromStartPos.y / maxDistanceAlongYAxis;
Vector3 pos = CalculateBezier(t);
transform.position = pos;
It's just an hint to put you in the right direction.
I tried with keyboard and its working fine,
but not with mouse or touch
using System;
using UnityEngine;
public class Collector : MonoBehaviour
{
public Transform startPoint;
public Transform middlePoint;
public Transform endPoint;
public float curveSpeed = 0.5f;
//public float speed = 0f;
private int _direction = 1;
private bool _isObjectSelected;
private Vector3 _mouseLastPosition;
private float _journeyLength;
private Vector3 _offsetPos;
private float _currentTime = 0;
private void Start()
{
_journeyLength = Vector3.Distance(startPoint.position,
endPoint.position);
UpdateJourney(0);
}
private void OnMouseDown()
{
if (_isObjectSelected)
return;
_offsetPos = Vector3.zero;
_mouseLastPosition = Input.mousePosition;
_isObjectSelected = true;
}
private void OnMouseUp()
{
_isObjectSelected = false;
}
private void OnMouseExit()
{
_isObjectSelected = false;
}
private void OnMouseDrag()
{
if (_isObjectSelected)
{
Debug.LogError("Mouse drag");
Vector3 currentPosition = Input.mousePosition;
_offsetPos += currentPosition - _mouseLastPosition;
float distCovered = _offsetPos.y / _journeyLength;
UpdateJourney(distCovered);
_mouseLastPosition = currentPosition;
}
}
private void UpdateJourney(float time)
{
if (time < 0)
time = 0;
else if (time > 1)
time = 1;
_currentTime = time;
transform.position =
QuadraticCurve(startPoint.position,
middlePoint.position,
endPoint.position,
_currentTime);
transform.rotation = Quaternion.Euler(
new Vector3(0, 0,
QuadraticCurve(0, 45, 90, _currentTime)));
}
private void Update()
{
// moving on path using keyboard input
float direction = Input.GetAxisRaw("Horizontal");
if (Math.Abs(direction) > 0.1f)
{
_currentTime += Time.deltaTime * curveSpeed * direction;
UpdateJourney(_currentTime);
}
}
private static Vector3 Lerp(Vector3 start, Vector3 end, float time)
{
return start + (end - start) * time;
}
private static Vector3 QuadraticCurve(Vector3 start, Vector3 middle, Vector3 end, float time)
{
Vector3 point0 = Lerp(start, middle, time);
Vector3 point1 = Lerp(middle, end, time);
return Lerp(point0, point1, time);
}
private static float QuadraticCurve(float start, float middle, float end, float time)
{
float point0 = Mathf.Lerp(start, middle, time);
float point1 = Mathf.Lerp(middle, end, time);
return Mathf.Lerp(point0, point1, time);
}
}

Resources