I want to move my boat to the position where i clicked BUT with a realistic movement and rotation :
(http://i.imgur.com/Pk8DOYP.gif)
Here is my code (attached to my boat gameobject) :
Basically, when i click somewhere, it move the boat until it reach the point that i clicked in the first place (i simplified my code for you)
using UnityEngine;
using System.Collections;
public class BoatMovement : MonoBehaviour {
private Vector3 targetPosition;
private float speed = 10f;
private bool isMoving;
void Update(){
if (!isMoving && Input.GetMouseButton (0)) {
targetPosition = Camera.main.ScreenToWorldPoint (Input.mousePosition);
isMoving = true;
}
if (isMoving) {
moveToPosition ();
}
}
void moveToPosition() {
transform.position = Vector3.MoveTowards (transform.position, new Vector3(targetPosition.x, targetPosition.y, 0f), speed * Time.deltaTime);
if (transform.position.x == targetPosition.x && transform.position.y == targetPosition.y) {
isMoving = false;
}
}
}
After some research and tries, i didn't find a way to do what i want.
Thanks for your help
There's two parts to this problem, which should be kept completely separate from each other and at no time does the equation of one interfere with the equation of the other.
The first is the forward movement of the ship, which can be achieved in two ways:
If you are using a rigidbody
void Propel()
{
float speed = 150f;
RigidBody rb = GetComponent<RigidBody>();
Transform transform = GetComponent<Transform>();
rb.AddForce(transform.forward * speed * Time.deltaTime); // <--- I always forget if its better to use transform.forward or Vector3.forward. Try both
}
If no rigidbody
void Propel()
{
float speed = 150f;
Transform transform = GetComponent<Transform>();
transform.Translate(transform.forward * speed * Time.deltaTime, Space.World);
}
Now the second would be the turning of the ship, which also can be achieved in two ways:
With rigidbody
IEnumerator TurnShip(Vector3 endAngle)
{
float threshold = Single.Epsilon;
float turnSpeed = 150f;
RigidBody rb = GetComponent<RigidBody>();
while (Vecotr3.Angle(transform.forward, endAngle) > threshold)
{
rb.AddTorque(transform.up * turnSpeed * Time.deltaTime);
yield return null;
}
}
Without rigidbody
IEnumerator TurnShip(Vector3 endAngle)
{
float threshold = Single.Epsilon;
float turnSpeed = 150f;
float step = turnSpeed * Time.deltaTime;
while (Vector3.Angle(transform.forward, endAngle) > threshold)
{
newDir = Vector3.RotateTowards(transform.forward, endAngle, step);
transform.rotation = Quaternion.LookRotation(newDir);
yield return null;
}
}
Then of course, the IEnumerators are called like so:
StartCoroutine(TurnShip(new Vector3(12f, 1f, 23f));
Couple things to note:
This is pseudo code, i haven't tested any of it so just know that making it work is up to you, i'm only providing you with the correct path to follow.
All the variables in the beginnings of the methods are meant to be global variables, so declare them where you please.
I've used a Transform called target here, just replace it with the Vector from your mouse click.
public Transform target;
private void Update()
{
transform.position += transform.forward * Time.deltaTime;
Vector3 targetDir = target.position - transform.position;
float step = Time.deltaTime / Mathf.PI;
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, step, 0.0f);
transform.rotation = Quaternion.LookRotation(newDir);
}
The key to this is the RotateTowards function and calculating a new vector to look towards on each frame based on the current position.
If your boats have some kind of rotation speed stat then modify the float called step to suit. If they have a speed stat then modify the transform.position += transform.forward * Time.deltaTime with some multiplier.
I guess you'll want to put some conditions in as to when to stop the movement and rotation, perhaps using a coroutine.
Declare
private Vector3 targetPosition;
private float targetDistance;
Then at the start of your move, do :
targetPosition = Camera.main.ScreenToWorldPoint (Input.mousePosition);
targetDistance = Vector3.Distance(targetPosition, transform.position);
While moving call this : (in your update loop for example)
turnSpeed = 0.062f * targetDistance;
moveSpeed = 35f * targetDistance;
//Important /!\ : you need to add Linear drag on your rigidbody or it will keep adding
RigidBody.AddForce(transform.up * moveSpeed * Time.deltaTime);
var newRotation = Quaternion.LookRotation (transform.position - targetPosition, Vector3.forward);
newRotation.x = 0f;
newRotation.y = 0f;
transform.rotation = Quaternion.Slerp (transform.rotation, newRotation, Time.deltaTime * turnSpeed);
Finally, add a linear drag value to your rigidbody (1 would be ok).
Thanks to maksymiuk who helped me and took a lot of his time trying to figure out a solution.
Related
I'm using Unity 3d version 4 because I have a MacOS X and I'm making a FPS 3D
I have a script to make the player shoot but this script only shoots up and does not consider where I point the mouse
Below the code in CS
Could someone please correct my script because I have been blocked for 3 days and I have not found any solution on the Internet. Thanks
using UnityEngine;
using System.Collections;
public class ShootDemo : MonoBehaviour {
public Rigidbody projectile;
public float speed = 100;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0))
{
Rigidbody instantiatedProjectile = Instantiate(projectile,
transform.position,
transform.rotation)
as Rigidbody;
instantiatedProjectile.velocity = transform.TransformDirection(new Vector3(0, 0,speed));
}
}
}
The way you set the velocity is not right, you should get the mouse position like this:
Rigidbody instantiatedProjectile = Instantiate(projectile, transform.position, transform.rotation);
var input = Input.mousePosition;
input.z = 10;
Vector2 touchPos = Camera.main.ScreenToWorldPoint(input);
Vector2 dir = new Vector2(transform.position.x, transform.position.y) - touchPos;
dir.Normalize();
instantiatedProjectile.velocity = dir * speed;
I found this script in JavaSript working but the bullet speed is too slow and then the bullet stays in the scene for 2 seconds instead of making it disappear completely. How can I then add strength to the bullet?
var Effect : Transform;
var TheDamage = 100;
function Update ()
{
var hit : RaycastHit;
var ray : Ray = Camera.main.ScreenPointToRay(Vector3(Screen.width*0.5, Screen.height*0.5, 0));
if (Input.GetMouseButtonDown(0))
{
if (Physics.Raycast (ray, hit, 100))
{
var particleClone = Instantiate(Effect, hit.point, Quaternion.LookRotation(hit.normal));
Destroy(particleClone.gameObject, 2);
hit.transform.SendMessage("ApplyDamage", TheDamage, SendMessageOptions.DontRequireReceiver);
}
}
}
I'm having a problem with the rotation of a game object.
I'm able to rotate the object in the direction/angle of another game object.
But after the first rotation the game object i'm rotating based on the delta rotation of the source game object It starts rotating in a different direction because it has been rotated once already I guess.
So is there a way so that after the first rotation I can reset the rotation so It moves again in the angle/direction the other object is rotating?
This is my current code below, i'm not a math expert so I might be doing things wrong ..
So the basic thing I want is that the second game object rotates in the same direction the base game object rotates but I don't want the rotation values to be equal, the rotation values doesn't have to be the same, it only has to rotate in the same angle/direction as the base game object rotates.
TiltAgainstObject is the source game object.
The script is attached to the second game object.
Any help is welcome :).
public class Tilt : MonoBehaviour {
public GameObject TiltAgainstObject;
private float rotX;
private float rotY;
private float rotZ;
private float rotW;
private float deltaX;
private float deltaY;
private float deltaZ;
private float deltaW;
// Use this for initialization
void Start () {
rotX = TiltAgainstObject.transform.rotation.x;
rotY = TiltAgainstObject.transform.rotation.y;
rotZ = TiltAgainstObject.transform.rotation.z;
rotW = TiltAgainstObject.transform.rotation.w;
}
// Update is called once per frame
void Update () {
if(OVRInput.Get(OVRInput.Button.One))
{
deltaX = TiltAgainstObject.transform.rotation.x - rotX;
deltaY = TiltAgainstObject.transform.rotation.y - rotY;
deltaZ = TiltAgainstObject.transform.rotation.z - rotZ;
deltaW = TiltAgainstObject.transform.rotation.w - rotW;
gameObject.transform.localRotation = new Quaternion(gameObject.transform.rotation.x + deltaX,
gameObject.transform.rotation.y + deltaY,
gameObject.transform.rotation.z + deltaZ,
gameObject.transform.rotation.w + deltaW);
}
rotX = TiltAgainstObject.transform.rotation.x;
rotY = TiltAgainstObject.transform.rotation.y;
rotZ = TiltAgainstObject.transform.rotation.z;
rotW = TiltAgainstObject.transform.rotation.w;
}
}
The question seems a bit confusing, bur if you want the second gameobject (where your script is) to rotate with the same direction of the first object you should do something like this:
public class Tilt : MonoBehaviour {
public GameObject TiltAgainstObject;
private float rotX;
private float rotY;
private float rotZ;
private float rotW;
private float deltaX;
private float deltaY;
private float deltaZ;
private float deltaW;
// Use this for initialization
void Start () {
rotX = TiltAgainstObject.transform.rotation.x;
rotY = TiltAgainstObject.transform.rotation.y;
rotZ = TiltAgainstObject.transform.rotation.z;
rotW = TiltAgainstObject.transform.rotation.w;
deltaX = 0;
deltaY = 0;
deltaZ = 0;
deltaW = 0;
}
// Update is called once per frame
void Update () {
rotX = TiltAgainstObject.transform.rotation.x;
rotY = TiltAgainstObject.transform.rotation.y;
rotZ = TiltAgainstObject.transform.rotation.z;
rotW = TiltAgainstObject.transform.rotation.w;
this.gameObject.transform.localRotation = new Quaternion(rotX + deltaX,
rotY + deltaY,
rotZ + deltaZ,
rotW + deltaW);
}
This way the second object will rotate in the same direction of the first object, if you want to change the speed of rotation just modify the deltas.
If you want the rotation to occur only when you press the button.One just pust it inside the if statement instead:
if(OVRInput.Get(OVRInput.Button.One))
{
this.gameObject.transform.localRotation = new Quaternion(rotX + deltaX,
rotY + deltaY,
rotZ + deltaZ,
rotW + deltaW);
}
I think the problem is that you're assigning the world coordinate rotation to your local coordinate localRotation, a typo maybe? Besides that though, I'm not sure why you're editing the components of the Quaternion directly, generally it's better to just add them together and use Unity's functions since dealing with 4 dimensions is often error-prone. You might try something like this:
public class Tilt : MonoBehaviour {
public GameObject TiltAgainstObject;
private Quaternion prevRotation;
private Quaternion currentRotation;
void Start()
{
prevRotation = TiltAgainstObject.transform.rotation;
}
void Update()
{
currentRotation = TiltAgainstObject.transform.rotation;
if (OVRInput.Get(OVRInput.Button.One))
gameObject.transform.rotation *= Quaternion.RotateTowards(prevRotation, currentRotation, 1000f);
prevRotation = currentRotation;
}
I´m newbie in Unity. I want rotate my 2D object based on user touch moved (moved finger on the screen). I have this code:
void Update ()
{
if (Input.touches.Length > 0) {
t = Input.GetTouch (0);
if (t.phase == TouchPhase.Moved) {
Vector3 movePos = new Vector3 (t.position.x, t.position.y, 0);
var objectPos = Camera.main.WorldToScreenPoint (transform.position);
var dir = movePos - objectPos;
transform.rotation = Quaternion.Euler (new Vector3 (0f, 0f, Mathf.Atan2 (dir.y, dir.x) * Mathf.Rad2Deg));
}
}
}
This code rotate the object based on user touch but when I touch screen again in another position and do touch move, it will rotate the whole object to the actual touch and then it will do correct object rotation based on touch move.
And I dont´t want rotate the whole object based on touch position but rotate the object only based on touch move. Do you understand me? Can you help me? How should I rewrite my code?
If I understand you, try to use this code below:
private float turnSpeed = 5f;
private Vector2 movement;
void Update()
{
Vector2 currentPosition = transform.position;
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
Vector2 moveTowards = Camera.main.ScreenToWorldPoint(touch.position);
movement = moveTowards - currentPosition;
movement.Normalize();
}
}
float targetAngle = Mathf.Atan2(movement.y, movement.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(0, 0, targetAngle), turnSpeed * Time.deltaTime);
}
Let me know if is what you want. Also, there is a complete sample here: https://github.com/joaokucera/unity-2d-object-rotation
Look into using deltaPosition instead of position on your touch. That should get you in the right direction.
var movedVector = t.deltaPosition;
Edit:
Here is a possible integration with your existing code. I don't have Unity on this PC so this is entirely untested. The main idea is you are getting a change in the finger position between frames. You then scale that change by move speed, and of course, the change in time between frame renders (delta time).
How the object rotates relative to that information is up to you. I just inserted the logic into your existing code.
float moveSpeed = 2.0f;
void Update ()
{
if (Input.touches.Length > 0) {
t = Input.GetTouch (0);
if (t.phase == TouchPhase.Moved) {
var delta = t.deltaPosition * moveSpeed * Time.deltaTime;
transform.rotation = Quaternion.Euler (new Vector3 (0f, 0f, Mathf.Atan2 (delta .y, delta.x) * Mathf.Rad2Deg));
}
}
}
As far as I know, DP is either you start with bigger problem and recursively come down, and keep saving the value each time for future use or you do it iteratively and keep saving values bottom up. But what if I am doing it bottom up but recursively going up?
Say for example the following question, Longest Common Subsequence
Here's my solution
public class LongestCommonSubseq {
/**
* #param args
*/
public static List<Character> list = new ArrayList<Character>();
public static int[][] M = new int[7][7];
public static void main(String[] args) {
String s1 = "ABCDGH";
String s2 = "AEDFHR";
for(int i=0;i<=6;i++)
for(int j=0;j<=6;j++)
M[i][j] = -1;
int max = getMax(s1,s2,0,0);
System.out.println(max);
Collections.sort(list);
for(int i = 0;i < max;i++)
System.out.println(list.get(i));
}
public static int getMax(String s1, String s2,int i ,int j){
if(i >= s1.length() || j>= s2.length()){
M[i][j] = 0;
return M[i][j];
}
if(M[i][j] != -1)
return M[i][j];
if(s1.charAt(i) == s2.charAt(j)){
M[i][j] = 1 + getMax(s1,s2,i+1,j+1);
list.add(s1.charAt(i));
}
else
M[i][j] = max(getMax(s1,s2,i+1,j) , getMax(s1, s2, i, j+1));
return M[i][j];
}
public static int max(int a,int b){
return a > b ? a : b;
}
}
So you see,I am going from M[0][0] in the other direction but I am not doing it iteratively.
But I guess it should be fine. Just needed to confirm.
Thanks
The direction does not matter. What is more important is that you go from more general(complex) problem to simpler ones. What you have done is dynamic programming.
For dynamic programming it doesn't matter if you follow the bottom-up or top-down-paradigm. The basic thesis (like you have correctly mentioned) of dynamic programming is known as Bellman's Principle of Optimality which is the following:
Principle of Optimality: An optimal policy has the property that
whatever the initial state and initial decision are, the remaining
decisions must constitute an optimal policy with regard to the state
resulting from the first decision.
Resource: Wikipedia (http://en.wikipedia.org/wiki/Bellman_equation#Bellman.27s_Principle_of_Optimality)
An great approach to cut of some of these optimal sub-solutions from the recursive-call-tree is to use Caching (like in your code).
I ask IN NUTITEQ, I know the way to perform this with GoogleApi with computeArea() but I have not found anything in Nutiteq sdk/snapshot.
Thanks in advance.
p.s. I know many methods to compute the area but I want to call something own of Nutiteq
EDIT:
There are no built-in Method, thanks for the fast answer Jaak, so I researched and found 2 Methods, both of them under WSG84 Projection. I developed a little programm to compare both of them and then I compared to a KML Tool, which computes area of a Polygon.
how-can-i-measure-area-from-geographic-coordinates
A-link-to-a-Geographic-Framework
And the results with a kml tool of University of New Hampshire
12197.38184
import java.lang.Math.*;
import net.sf.geographiclib.*;
public class ComputeAreaTest {
private static double[][] moorwiese_coords = {{12.925634f,48.427168f},
{12.926825f,48.427217f},
{12.926788f,48.428385f},
{12.926069f,48.428374f},
{12.925431f,48.42825f},
{12.925624f,48.427192f},
{12.925634f,48.427168f}};
protected static double computeArea() {
double area=0.0;
double earthRadius = 6378137.0f;
int size = moorwiese_coords.length;
if (size > 2) {
double[] p1 = new double[2];
double[] p2 = new double[2];
for (int i=0; i<size-1; i++) {
p1 = moorwiese_coords[i];
p2 = moorwiese_coords[i+1];
area += Math.toRadians(p2[0] - p1[0]) * (2 +
Math.sin(Math.toRadians(p1[1]) ) + Math.sin(Math.toRadians(p2[1])) );
}
area = area * earthRadius * earthRadius / 2.0;
}
return area;
}
protected static double computeAreaWithGeographicLib() {
int size = moorwiese_coords.length;
PolygonArea p = new PolygonArea(Geodesic.WGS84, false);
try {
for (int i=0;i<size;i++) {
p.AddPoint(moorwiese_coords[i][4], moorwiese_coords[i][0]);
}
}
catch (Exception e) {}
PolygonResult r = p.Compute();
return r.area;
}
public static void main(String[] args) {
double areaGeoLib = computeAreaWithGeographicLib();
double area = computeArea();
System.out.println("Area: " + (-1)*area + "\nArea(GeoLib): "+areaGeoLib);
}
}
Output
Area: 12245.282587113787
Area(GeoLib): 12254.95547034964
I found not very suitable for accurate use ( Yes, under 0.5% Error may be unaccurate for many environments) but useful to learn how to compute the area of a irregular Polygon.
Thx, Jaak, I had not seen this freen Symbol to click "Right Answer", now I have seen, I must explicitly. So, an "answer" is in initial post.