2D projectile trajectory prediction (unity3d) - unity3d

(Using unity3d 4.3 2d, it uses box2d like physics).
I have problems with predicting trajectory
I'm using:
Vector2 startPos;
float power = 10.0f;
float interval = 1/30.0f;
GameObject[] ind;
void Start (){
transform.rigidbody2D.isKinematic = true;
ind = new GameObject[dots];
for(int i = 0; i<dots; i++){
GameObject dot = (GameObject)Instantiate(Dot);
dot.renderer.enabled = false;
ind[i] = dot;
}
}
void Update (){
if(shot) return;
if(Input.GetAxis("Fire1") == 1){
if(!aiming){
aiming = true;
startPos = Input.mousePosition;
ShowPath();
}
else{
CalculatePath();
}
}
else if(aiming && !shot){
transform.rigidbody2D.isKinematic = false;
transform.rigidbody2D.AddForce(GetForce(Input.mous ePosition));
shot = true;
aiming = false;
HidePath();
}
}
Vector2 GetForce(Vector3 mouse){
return (new Vector2(startPos.x, startPos.y)- new Vector2(mouse.x, mouse.y))*power;
}
void CalculatePath(){
ind[0].transform.position = transform.position; //set frist dot to ball position
Vector2 vel = GetForce(Input.mousePosition); //get velocity
for(int i = 1; i < dots; i++){
ind[i].renderer.enabled = true; //make them visible
Vector3 point = PathPoint(transform.position, vel, i); //get position of the dot
point.z = -1.0f;
ind[i].transform.position = point;
}
}
Vector2 PathPoint(Vector2 startP, Vector2 startVel, int n){
//Standard formula for trajectory prediction
float t = interval;
Vector2 stepVelocity = t*startVel;
Vector2 StepGravity = t*t*Physics.gravity;
Vector2 whattoreturn = ((startP + (n * stepVelocity)+(n*n+n)*StepGravity) * 0.5f);
return whattoreturn;
}
Using this, I get wrong trajectory.
1. It's like gravity doesn't drag trajectory down at all, and yes i know that gravity is weak because:
t*t*Physics.gravity = 0.03^2 * vector2(0, -9.8) = vector2(0, -0.00882)
But that is the formula :S
2. Since gravity is low, velocity is too strong.
Here is the video:
http://tinypic.com/player.php?v=1z50w3m&s=5
Trajectory formula form:
http://www.iforce2d.net/b2dtut/projected-trajectory
What should I do?
I found that if I set
StepGravity to something stronger like (0, -0.1)
and devide startVel by 8
I get nearly right trajectory, but i don't want that, I need true trajectory path.
Users from answer.unity3d.com said I should ask here, because here is a bigger group of mathematical coders.
And I searched a lot about this problem (that how I found that formula).

you're only calculating the effect of gravity over 1/30th of a second for each step - you need to do it cumulatively. Step 1 should end with a velocity of 0.09G, Step 2 with .18G, step3 with .27G etc.
Here's a very simple example that draws the ballistic trajectory based on start velocity and a supplied time:
using UnityEngine;
using System.Collections;
public class grav : MonoBehaviour {
public Vector3 StartVelocity;
public float PredictionTime;
private Vector3 G;
void OnDrawGizmos()
{
if (G == Vector3.zero)
{
// a hacky way of making sure this gets initialized in editor too...
// this assumes 60 samples / sec
G = new Vector3(0,-9.8f,0) / 360f;
}
Vector3 momentum = StartVelocity;
Vector3 pos = gameObject.transform.position;
Vector3 last = gameObject.transform.position;
for (int i = 0; i < (int) (PredictionTime * 60); i++)
{
momentum += G;
pos += momentum;
Gizmos.DrawLine(last, pos);
last = pos;
}
}
}
In you version you'd want draw your dots where I'm drawing the Gizmo, but it's the same idea unless I'm misunderstanding your problem.

Related

Bad usage of Physics.OverlapBox

I don't understand the usage of method Physics.OverlapBox.
I want to put ten walls on a 4x4 plane in my scene. The width and location of the walls is calculated randomly. In the odd number of cycles in the creation loop the wall is rotated 90 degrees.
The walls presented in the scene should not collide with other walls... but it's not working.
void Reset()
{
int walls = 10;
for (int w = 0; w < walls; w++)
{
for (int i = 0; i < 5; i++)
{
float x = Random.Range(-20f, 20f);
float z = Random.Range(-20f, 20f);
Vector3 center = new Vector3(x, 1.51f, z);
int scalex = Random.Range(4, 13);
Quaternion quaternion = Quaternion.identity;
if (w % 2 == 1)
quaternion=Quaternion.Euler(0, 0, 0);
else
quaternion=Quaternion.Euler(0, 90, 0);
Collider[] colliders = Physics.OverlapBox(center, new Vector3(scalex, 3, 1) / 2, quaternion);
Debug.Log(colliders.Length);
if (colliders.Length == 0)
{
GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
wall.transform.position = center;
wall.transform.localScale = new Vector3(scalex, 3, 1);
wall.transform.rotation = quaternion;
wall.tag = "wall";
break;
}
}
}
}
After your comment I think I now know what the issue is:
The physics engine simply doesn't "know" your walls colliders yet since the physics engine is updated in the next FixedUpdate.
You might want to call Physics.Simulate after each wall in order to manually trigger a physics update.
The docs are a bit unclear if it is necessary but you might have to disable Physics.autoSimulation before the loop and turn it back on after you are finished with the wall creation.
The solution proposed by derHugo (a lot of thanks) works from Start method. I added rotation to the walls. No collisions.
The code:
public void Start()
{
Physics.autoSimulation = false;
for (int i = 0; i < 200; i++)
{
createWall();
Physics.Simulate(Time.fixedDeltaTime);
}
Physics.autoSimulation = true;
}
void createWall()
{
float x = Random.Range(-20f, 20f);
float z = Random.Range(-20f, 20f);
Vector3 position = new Vector3(x, 1.51f, z);
Quaternion rotation = Quaternion.Euler(0, Random.Range(0,360), 0);
int scalex = Random.Range(2, 5);
Collider[] colliders = Physics.OverlapBox(position, new Vector3(scalex, 1, 1)/2, rotation);
if (colliders.Length==0)
{
GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
wall.transform.localPosition = position;
wall.transform.localScale = new Vector3(scalex, 3, 1);
wall.transform.rotation = rotation;
wall.tag = "wall";
}
}

Unity - character falling faster than jumping

I'm trying to implement my own gravity for a character.
But for some unknown to me reason the character's landing speed is higher landing than when jumping, by quite a lot (~15 initial jump speed, ~24 final land speed). I'm ultimately trying to replicate behavior shown on the gif below.
https://imgur.com/a/q77w5kS
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Movement : MonoBehaviour {
public float speed = 3;
public float jumpSpeed = 15;
public float gravity = -1f;
private float ySpeed = 0;
private float jumpTime = 0;
private bool direction = false; //true for going up, false for falling down - gravity
private bool previousDirection = false; //to keep track of changing direction
private CharacterController _characterController;
// Start is called before the first frame update
void Start() {
_characterController = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update() {
float dx = Input.GetAxis("Horizontal");
float dz = Input.GetAxis("Vertical");
float dy = Input.GetAxis("Jump");
// move around
Vector3 movement = new Vector3(dx, 0, dz);
// limit diagonal movement
movement = Vector3.ClampMagnitude(movement, 1);
// speed
movement *= speed;
// change of direction
if(ySpeed>0 && direction!=true)
direction = true;
else if(ySpeed<0 && direction!=false)
direction = false;
// if changed direction - peak of the jump
if(direction!=previousDirection){
Debug.Log("Jump Time = " + jumpTime);
jumpTime = Time.deltaTime;
previousDirection = direction;
}
// jump/on ground
if(_characterController.isGrounded) {
ySpeed = -0.5f;
jumpTime = 0;
if(dy > 0f){
ySpeed = jumpSpeed;
}
}
// gravity - when not on ground
else{
// dv/dt
ySpeed += gravity*jumpTime;
// add jump time
jumpTime += Time.deltaTime;
// Debug.Log(jumpTime);
Debug.Log(ySpeed);
}
movement.y = ySpeed;
// direction adjustment
movement = transform.TransformDirection(movement);
movement *= Time.deltaTime;
_characterController.Move(movement);
}
}
I think I figured out what was wrong with my idea.
ySpeed += gravity*jumpTime
So every frame I'm adding more and more acceleration downwards. This should just be: ySpeed += gravity *Time.deltaTime
(Acceleration due to gravity is then constant, not getting greater as time passes)
It is being integrated over many steps, so each Update() cycle is a slice of time that adds some velocity based on acceleration due to gravity, multiplied by the amount of time taken by that little slice.
In another words...
Gravity is a constant acceleration. I've made it a linear function of the time spent in the air. So, I start the jump with no gravity and end the jump with very high gravity.

Snapped GO's Rotation not working properly

I'm currently trying to snap objects (cubes, pyramids, torus) together. I assigned each face of the objects bounding box a "snap allowed" variable and on collision try to find the closest two sides to each other. I then move the main object to the collision side and rotate it accordingly.
The code is as follows:
private static Vector3[] sides = {
Vector3.up,
Vector3.forward,
Vector3.right,
Vector3.down,
Vector3.back,
Vector3.left
};
private void Snap() {
if (!snappedObject)
return;
if (lastSnapping > LeftController.GetLastSnappingToggle()) {
//we dont calculate new sides, only use the old ones
//but we have to check whether we are now further away
float dis = Vector3.Distance(GetWorldPositionFace(this.gameObject, lastSelf),
GetWorldPositionFace(snappedObject, lastOther));
float max = Mathf.Max(Mathf.Abs(size.x), Mathf.Max(Mathf.Abs(size.y), Mathf.Abs(size.z)));
if (dis > max)
return;
ApplyToOther(lastSelf, lastOther, snappedObject);
} else {
//we need to find both new closest sides
MeshSnapping other = snappedObject.GetComponent<MeshSnapping>();
float otherDis = float.MaxValue;
int otherSide = -1;
//find the closest side from the other object
for (int i = 0; i < NUM_SIDES; i++) {
float dis = Vector3.Distance(transform.position, GetWorldPositionFace(snappedObject, i));
if (dis < otherDis && other.sidesAllowed[i]) {
otherDis = dis;
otherSide = i;
}
}
//find the closest side of our object
float selfDis = float.MaxValue;
int selfSide = 0;
for (int i = 0; i < NUM_SIDES; i++) {
float dis = Vector3.Distance(GetWorldPositionFace(this.gameObject, i),
GetWorldPositionFace(snappedObject, otherSide));
if (dis < selfDis && sidesAllowed[i]) {
selfDis = dis;
selfSide = i;
}
}
//are we to far away or at a prohibited side?
float max = Mathf.Max(Mathf.Abs(size.x), Mathf.Max(Mathf.Abs(size.y), Mathf.Abs(size.z)));
if (selfDis > max)
return;
ApplyToOther(selfSide, otherSide, snappedObject);
//save the sides for next iteration
lastSelf = selfSide;
lastOther = otherSide;
}
lastSnapping = Time.time;
}
private void OnCollisionEnter(Collision collision) {
snappedObject = collision.gameObject;
}
private void OnCollisionExit(Collision collision) {
snappedObject = null;
}
private Vector3 GetWorldPositionFace(GameObject other, int i) {
//get the side in local coordinates, scaled to size
Vector3 otherLocalSize = other.transform.localScale;
Vector3 otherSidePoint = new Vector3(otherLocalSize.x * sides[i].x, otherLocalSize.y * sides[i].y, otherLocalSize.z * sides[i].z) / 2f;
//rotate it according to world position
Vector3 dir = (other.transform.rotation * otherSidePoint);
//actually move it to world position
Vector3 center = other.transform.position + dir;
return center;
}
private void ApplyToOther(int selfI, int otherI, GameObject other) {
//first get the midpoint of face of other go
Vector3 edge = GetWorldPositionFace(other, otherI);
Vector3 dir = edge - other.transform.position;
RotateSides(selfI, otherI, dir);
selfI = (selfI + NUM_SIDES / 2) % NUM_SIDES;
//get midpoint of face of self go
edge += GetWorldPositionFace(this.gameObject, selfI) - transform.position;
//now move towards the combination
transform.position = edge;
}
private void RotateSides(int selfI, int otherI, Vector3 dir) {
//rotate self side towards this point
switch (selfI) {
case 0: transform.up = -dir; break;
case 1: transform.forward = -dir; break;
case 2: transform.right = -dir; break;
case 3: transform.up = dir; break;
case 4: transform.forward = dir; break;
case 5: transform.right = dir; break;
}
}
I can find every midpoint of the bounding box by transforming the direction vector, applying the objects current rotation and position to it (see GetWorldPositionFace() ). After finding the best combination, ApplyToOther() moves the objects to position and rotates it according to the selected face normals. So far so good, but the result is not aligned. As you can see, the front faces do not face in the same direction, i.e. I want to rotate the upper cube around the transform.up axis by this amount. This would be the result that I want.
But, if I add
float angle = Vector3.Angle(transform.forward, snappedObject.transform.forward);
transform.Rotate(transform.up, angle);
to the RotateSides() function, the result is this. The rotation axis is wrong.
Using
Quaternion.FromToRotation(transform.up, snappedObject.transform.up)
did not work either.
What did I miss? Thanks for your help!
I figured out my problem. By setting the transform.forward and the transform.up seperately (e.g. with transform.rotate around axis), only one of them was correct. Using Quaternion.LookRotation() solves this.

How to draw a line over a specific duration using Vectrosity [duplicate]

I created a diagonal line renderer by attaching the following script to an empty game object. How can I extend the line at both ends for a half its length and how can I also extend the line by say 1 unit along the x-axis? Both over a certain period of time.
public class DiagonalLine : MonoBehaviour {
bool firstLineComplete = false;
LineRenderer diagLine;
public Vector3 startPoint = new Vector3 (0, 0, 0);
public Vector3 endPoint = new Vector3 (1.0f, 1.0f, 0);
public float lineDrawSpeed;
// Use this for initialization
void Start () {
diagLine = gameObject.AddComponent<LineRenderer>();
diagLine.material = new Material (Shader.Find ("Sprites/Default"));
diagLine.startColor = diagLine.endColor = Color.green;
diagLine.startWidth = diagLine.endWidth = 0.15f;
diagLine.SetPosition (0, startPoint);
diagLine.SetPosition (1, endPoint);
}
}
This is basic vector math.
You have the line with (from end to start):
Vector3 v = start - end;
and then you extend on each side by half if it:
extensionA = start + (v * 0.5f);
extensionB = end + (v * -0.5f);
If you need to extend by 1 then normalize:
Vector3 v = (start - end).normalized;
extensionA = start + v;
extensionB = end + (v * -1f);
Break your problem into pieces:
1.Extend line by x units on both sides:
This is done with the Ray class. Create a new Ray instance from the startPoint and endPoint variables then use the Ray.GetPoint function to extend the line. You have to do this on both sides to get the new extended lines.
A simple wrapper for the Ray class to simplify this:
Vector3 extendLine(Vector3 startPoint, Vector3 endPoint, ExtendDirection extendDirection, float extendDistance)
{
Ray ray = new Ray();
//Start
if (extendDirection == ExtendDirection.START_POINT)
{
ray.origin = startPoint;
ray.direction = startPoint - endPoint;
}
//End
else if (extendDirection == ExtendDirection.END_POINT)
{
ray.origin = endPoint;
ray.direction = endPoint - startPoint;
}
//Extend
Vector3 newUnityPoint = ray.GetPoint(extendDistance);
//Debug.DrawLine(ray.origin, newUnityPoint, Color.blue);
return newUnityPoint;
}
public enum ExtendDirection
{
START_POINT, END_POINT
}
Extend to the Left end
Vector3 newStartPos = extendLine(startPoint, endPoint, ExtendDirection.START_POINT, 4);
diagLine.SetPosition(0, newStartPos);
Extend to the Right end
Vector3 newEndPos = extendLine(startPoint, endPoint, ExtendDirection.END_POINT, 4);
diagLine.SetPosition(1, newEndPos);
2.For animating/moving it over time, use coroutine and Time.deltaTime. Increment a variable with Time.deltaTime every frame to then use Vector3.Lerp to lerp the from and to value.
See this function for example.
With both combined, below is a complete function to extend both lines over time:
bool isRunning = false;
IEnumerator extentLineOverTime(LineRenderer targetLineRenderer, float extendDistance, float duration)
{
//Calculate Left from extension length
Vector3 fromValLeftPos = targetLineRenderer.GetPosition(0);
//Calculate Right from extension length
Vector3 fromValRightPos = targetLineRenderer.GetPosition(1);
//Calculate Left to extension length
Vector3 newLeftPos = extendLine(fromValLeftPos, fromValRightPos, ExtendDirection.START_POINT, extendDistance);
//Calculate Right to extension length
Vector3 newRightPos = extendLine(fromValLeftPos, fromValRightPos, ExtendDirection.END_POINT, extendDistance);
//Make sure there is only one instance of this function running
if (isRunning)
{
yield break; ///exit if this is still running
}
isRunning = true;
float counter = 0;
while (counter < duration)
{
counter += Time.deltaTime;
//Move to left overtime
Vector3 tempLeftPos = Vector3.Lerp(fromValLeftPos, newLeftPos, counter / duration);
targetLineRenderer.SetPosition(0, tempLeftPos);
//Move to Right overtime
Vector3 tempRightPos = Vector3.Lerp(fromValRightPos, newRightPos, counter / duration);
targetLineRenderer.SetPosition(1, tempRightPos);
yield return null;
}
isRunning = false;
}
USAGE:
LineRenderer diagLine;
public Vector3 startPoint = new Vector3(0, 0, 0);
public Vector3 endPoint = new Vector3(1.0f, 1.0f, 0);
// Use this for initialization
void Start()
{
diagLine = gameObject.AddComponent<LineRenderer>();
diagLine.material = new Material(Shader.Find("Sprites/Default"));
diagLine.startColor = diagLine.endColor = Color.green;
diagLine.startWidth = diagLine.endWidth = 0.15f;
diagLine.SetPosition(0, startPoint);
diagLine.SetPosition(1, endPoint);
//Extend Line Over time
StartCoroutine(extentLineOverTime(diagLine, 4, 3));
}
The StartCoroutine(extentLineOverTime(diagLine, 4, 3)); will extend the line 4 units away from both sides within 3 seconds.

object movement as wave in Unity 3d

I created a object in unity
GameObject monsterclone =
(GameObject)Instantiate(monsterPrefab, floorPosition, Quaternion.identity);
This object should move in a wave style from a limit1 to limit2.
Then move back from limit2 to limit1.
Y position as well x position have to change in a specific way.
Vector3 nPos = mfloorPos + new Vector3(2f, 0f, 0f);
Vector3 oPos = mfloorPos + new Vector3(-2f, 0f, 0f);
How can I do it?
I can't exactly write the code without knowing more specific but i think this question is already asked any this link will help you out MOVE OBJECT as wave
EDIT:
I think the flat up and float down functionality will work for you for moving one point to another
example:
var floatup;
function Start(){
floatup = false;
}
function Update(){
if(floatup)
floatingup();
else if(!floatup)
floatingdown();
}
function floatingup(){
transform.position.y += 0.3 * Time.deltaTime;
yield WaitForSeconds(1);
floatup = false;
}
function floatingdown(){
transform.position.y -= 0.3 * Time.deltaTime;;
yield WaitForSeconds(1);
floatup = true;
}
example taken from
float amplitudeX = -25.0f;
float amplitudeY = 5.0f;
float omegaX = 0.5f;
float omegaY = 4.0f;
float index;
void Update () {
index += Time.deltaTime;
float x = amplitudeX*Mathf.Cos (omegaX*index);
float y = Mathf.Abs (amplitudeY*Mathf.Sin (omegaY*index));
if(transform.position.x > 24){
transform.eulerAngles = new Vector3(270, -90, 0);
}
if(transform.position.x < -24){
transform.eulerAngles = new Vector3(270, 90, 0);
}
transform.localPosition= new Vector3(x,y,20);
}
If this is a consitant wave and not dependant on speed I would use an animation to create a literal wave curve of the Position.Y value (much in the same principle as Ravindra Shekhawat has explained.) you can find out more about animation here.
Here is some code (untested) that you could go off. It is in c# so I hope it proves no issue with putting in to JavaScript.
bool monsterMoving = false;
void Update(){
//check monster moving to start updating position
if(monsterMoving == true){
//moving animation controls up and down "wave" movement
animation.CrossFade('moving');
//Lerp changes position
Transform.Lerp(transform.Position, oPos, Time.deltaTime);
if (transform.Position == oPos) {
//We are at destination, stop movement
monsterMoving = false;
}
} else {
// if monster stopped moving, return to idle animation (staying still)
animation.CrossFade('idle');
}
}
// function to send a new position to the monster object
void MoveTo(Vector3 newPos){
oPos = newPos;
monsterMoving = true;
}