I have been trying to find a solution to my problem involving a game object in my scene that walks through elevated terrain and rocks. The rocks have colliders and the enemy object has a collider and a rigid body. I have attached my script which moves the enemy around when the FPS gets withing a certain range. This all works fine but its walking through rocks and elevated terrain. I have tried many suggestions so far but nothing seems to work.
using UnityEngine;
using System.Collections;
public class CreatureWalk : MonoBehaviour {
public int totalSecs = 0;
public GameObject explosion;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
GameObject GM = GameObject.Find ("GameManager");
//Get Game Manager Component on Object
GameManager GM_Component = GM.GetComponent<GameManager> ();
if (GM_Component.GameOver) {
animation.Play ("idle");
} else if (GM_Component.FlowerHits > 30) {
while (totalSecs < 5000) {
animation.Play ("hit");
totalSecs += 1;
}
//Destroy (gameObject);
//create explosion
Instantiate (explosion, transform.position, transform.rotation);
Invoke("Disable",3);
Invoke("Reenable",3);
Vector3 currentPosition = this.transform.position; //get current position of creature
transform.position = new Vector3(currentPosition.x+ Random.Range(-20.0F, 20.0F),0.2f,currentPosition.z +Random.Range(-20.0F, 20.0F));
totalSecs =0;
GM_Component.FlowerHits =0;
} else {
animation.Play ("walk");
MoveTowardsTarget ();
}
}
private void Disable() {
gameObject.SetActive(false);
//Debug.Log ("Disable");
}
private void Reenable() {
gameObject.SetActive(true);
//Debug.Log ("Re-enable");
}
//move towards a target at a set speed.
private void MoveTowardsTarget() {
//the speed, in units per second, we want to move towards the target
float speed = 1;
//move towards the fps
Vector3 targetPosition = new Vector3(Camera.main.transform.position.x,0,Camera.main.transform.position.z);
Vector3 currentPosition = this.transform.position;
transform.LookAt (targetPosition);
//first, check to see if we're close enough to the target
if (Vector3.Distance (currentPosition, targetPosition) < 30f) {
Vector3 directionOfTravel = targetPosition - currentPosition;
directionOfTravel.Normalize ();
this.transform.Translate (
(directionOfTravel.x * speed * Time.deltaTime),
(directionOfTravel.y * speed * Time.deltaTime),
(directionOfTravel.z * speed * Time.deltaTime),
Space.World);
//this.rigidbody.MovePosition(directionOfTravel);
// (directionOfTravel.x * speed * Time.deltaTime),
// (directionOfTravel.y * speed * Time.deltaTime),
// (directionOfTravel.z * speed * Time.deltaTime));
//this.rigidbody.AddForce(
// (directionOfTravel.x * speed * Time.deltaTime),
// (directionOfTravel.y * speed * Time.deltaTime),
// (directionOfTravel.z * speed * Time.deltaTime),ForceMode.VelocityChange);
//rigidbody.AddForce(transform.forward * 10);
} else
animation.Play ("idle");
}
}
I have tried to move with AddForce as suggested as you can see by the commented code but the enemy just walks in place.
The creature has a rigidbody, gravity and kinematics checked.
Try adding 1000 or 10000 force ... from the looks of it you're using fractions of 1... if you have a mass of 1 it still takes over 1 force to make it move. If you add 1000 force and his mass is 1 and he still doesn't move, then make sure he's not set as kinematic in his rigidbody.
If you still aren't moving, something much more insidious is going on. I'd suggest checking you're using the right axis, the right direction.. etc.. things that just couldn't possibly be wrong.
Good luck, hope that helped.
Whenever you're using AddForce, try to multiply the directional Vector with your desired speed and the object's Rigidbody mass. Also, uncheck the "isKinematic" checkbox because that kind of "deactivates" the physics engine's calculations on that object. If you want to be accurate you can also manually set the velocity of the object's Rigidbody too.
Related
Currently I'm using a normalized rb velocity to get the x and z velocity directions in order to compare it with eulerAngles of the camera in order to tell the camera which way to spin. But I'm doing this inside a bunch of nested if statements and it just feels.... wrong.
For actually following the ball I just have parent child relationships with Player -> RotatorObject -> Camera, so rotating the RotatorObject rotates around the center of the player.
Here is an example of what I'm currently doing.
// Velocity direction of the ball
Vector3 VelNormed = Vector3.Normalize(rb.velocity);
xVelNormed = VelNormed.x;
zVelNormed = VelNormed.z;
// Current CameraRotationObject angle
yEuler = transform.eulerAngles.y;
zEuler = transform.eulerAngles.z;
// If Moving forwards.
if (zVelNormed >= 0.5)
{
// If camera is within hoped for range. (270 is camera = perfectly forwards)
if (yEuler >= 260 && yEuler <= 280)
{
// Move camera to the right.
if (yEuler >= 260 && yEuler <= 270)
{
// Rotate camera right
transform.Rotate(0, (speed * Time.deltaTime) * 1, 0);
}
else if (yEuler <= 280 && yEuler >= 270)
{
// Rotate camera left
transform.Rotate(0, (speed * Time.deltaTime) * -1, 0);
}
else
{
}
}
}
// If Moving backwards.
else if (zVelNormed <= -0.5)
{
//etc.
}
Like I said before, using all of these if statements like this makes me feel like a monster! IE. I'm almost sure I'm doing this the old computer programming for dummies way
First of all the eulerAngles are very rarely what you want to use
When you read the .eulerAngles property, Unity converts the
Quaternion's internal representation of the rotation to Euler angles.
Because, there is more than one way to represent any given rotation
using Euler angles, the values you read back out may be quite
different from the values you assigned. This can cause confusion if
you are trying to gradually increment the values to produce animation.
Why do you actually need to make the camera spin at all? It sounds to me like your main issue actually origins in the camera being somewhere nested as child below the rolling ball.
You should rather extract the camera into scene root level and make it follow the ball via code like e.g.
public class CameraFollow : MonoBehaviour
{
// Interpolation factors for the position and rotation
[Range(0, 1)] public float moveInterpolationFactor = 5f;
[Range(0, 1)] public float rotateInterpolationFactor = 5f;
// The target this object shall follow
[SerializeField] Rigidbody targetObject;
// Positional offset in the movement direction of the target object
// e.g. (0,0, -1) will keep the camera 1 world space unit behind the move direction
public Vector3 positionOffset = new Vector3(0, 0.25f, -1);
private Vector3 targetPosition;
private Quaternion targetRotation;
private void Awake ()
{
if(!targetObject) return;
UpdateTargetValues();
}
private void UpdateTargetValues ()
{
targetPosition = targetObject.position + targetObject.rotation * positionOffset;
targetRotation = Quaternion.LookRotation(targetObject.velocity);
}
private void LateUpdate ()
{
// If there is no target stay where you are
if(!targetObject) return;
// Get the move velocity since we want to follow based
// on the move direction, not the objects orientation
var velocity = targetObject.velocity;
// If not moving simply continue moving to the last set position
// otherwise update the target values
if(velocity.sqrMagnitude > 0)
{
UpdateTargetValues ();
}
transform.position = Vector3.Slerp(transform.position, targetPosition, moveInterpolationFactor * Time.deltaTime);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotateInterpolationFactor * Time.deltaTime);
}
public void SetTarget(Rigidbody target)
{
targetObject = target;
if(!targetObject) return;
UpdateTargetValues ();
}
}
This is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
// Tweekable move speed
public float MoveSpeed = 4;
// Set what distance it should be from the target
int MaxDist = 10;
int MinDist = 5;
// This is the target
public Transform Mouse;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
// Keep looking at an object
transform.LookAt();
// Turn off gravity
Physics2D.gravity = Vector2.zero;
// When distance is over minimun
if (Vector3.Distance(Mouse.position, Mouse.position) >= MinDist)
{
// Keep distance
transform.position += transform.forward * MoveSpeed * Time.deltaTime;
// When it's between max and minimun
if (Vector3.Distance(transform.position, Mouse.position) <= MaxDist)
{
}
}
}
// I imported this code from another project so the enemy keeps looking at the player, and keeps his distance. it was a 3D project, so there may be more errors, and this is also my first time creating a game. Be gentle
I am working on a similar project right now.
Here is the code I use to rotate the player (first part) and then move it into that direction (second part):
public Rigidbody2D rb;
public float speed;
void Update()
{
//get mouse position
Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//calculating the angle
float AngleRad = Mathf.Atan2(mousePos.y - transform.position.y, mousePos.x -
transform.position.x);
float AngleDeg = (180 / Mathf.PI) * AngleRad;
//rotating the player
Player.transform.rotation = Quaternion.Euler(0, 0, AngleDeg);
//second part
Vector3 mouseDir = mousePos - transform.position;
mouseDir.z = 0f;
mouseDir = mouseDir.normalized;
rb.AddForce(mouseDir * speed);
}
If the player rotates, but it is looking in the wrong way (in my case), then play around with the the last line of the first part. The following code worked fine for me:
transform.rotation = Quaternion.Euler(0, 0, AngleDeg - 90);
If you want the player to move at a constant speed, then you can use
rb.velocity = mouseDir * speed;
instead of
rb.AddForce(mouseDir * speed);
in the last line of the second part.
Your Player-Gameobject needs a Rigidbody2D component. I also added an if-statement around the second part, so the player only moves to the wanted direction when (for example) space is pressed, but always looks to the mouse.
I want achieve moving object on x axis only with lerp to get smoothly movement.
this is picture what i need
I don't know how i can implement lerp to this code to get smooth movement between these values, it now works but it teleport the player and that is not smooth movement what i want to achieve
This is my working code that teleports player:
void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 desiredPos = new Vector3(transform.position.x + 1.5f, transform.position.y, transform.position.z);
transform.position = desiredPos;
}
if (Input.GetMouseButtonDown(1))
{
Vector3 desiredPos = new Vector3(transform.position.x -1.5f, transform.position.y, transform.position.z);
transform.position = desiredPos;
}
}
I want to implement this but i don't understand how to do it .. When i put all code into update the player don't even move.. It only works for me when i copy paste all the code from docs, but how i can move the time from start method to update and always do the same to achieve to get smooth movement for player when going left and right i don't know really please help me guys..
This is the code that works but i don't know how to change it for my example..
https://docs.unity3d.com/ScriptReference/Vector3.Lerp.html
There are multiple ways. I would not use Translate as this gives you little control here but rather e.g. MoveTowards which makes sure you have no over shooting at the end. Use this for a linear movement with a given moveSpeed:
// set move speed in Units/seconds in the Inspector
public float moveSpeed = 1f;
private Vector3 desiredPos;
private bool isMoving;
private void Update()
{
if (!isMoving && Input.GetMouseButtonDown(0))
{
desiredPos = transform.position + Vector3.right * 1.5f;
isMoving = true;
}
if (!isMoving && Input.GetMouseButtonDown(1))
{
desiredPos = transform.position - Vector3.right * 1.5f;
isMoving = true;
}
if(isMoving)
{
transform.position = Vector3.MoveTowards(transform.position, desiredPos, moveSpeed * Time.deltaTime);
// this == is true if the difference between both
// vectors is smaller than 0.00001
if(transform.position == desiredPos)
{
isMoving = false;
// So in order to eliminate any remaining difference
// make sure to set it to the correct target position
transform.position = desiredPos;
}
}
}
Or as you asked use Vector3.Lerp like e.g.
// a factor between 0 and 1
[Range(0, 1)] public float lerpFactor;
...
transform.position = Vector3.Lerp(transform.position, desiredPos, lerpFactor);
lerpFactor has to be a value between 0 and 1 where in our case 0 would meen the object never moves and 1 it directly jumps to the target position. In other words the closer you set it to 0 the slower it will reach the target, the closer you set it to 1 the faster it will reach the target.
a lot of people do this to get "smooth" movements but what actually happens is e.g. if you set 0.5 for lerpFactor then every frame the object is placed in the middle between current position and target position.
That looks somehow smooth, moves very fast at the beginning and very very slow at the end ... but: It actually never really reaches the target position but just gets very slow.
For your case that is fine since anyway we compare the current and target position using == with a precision of 0.00001. One just has to have in mind how Lerp works.
But with this you won't have any control neither over the move speed nor the duration.
If you want overall more controll (as I do) I would recommend to use a Coroutine (it is not absolutely neccessary and you could do the same in Update as well but in my eyes Coroutines are better to maintain and keep track of).
Than you could also make a smooth eased-in and eased-out movement with an always fixed duration regardless how far the distance is
// set desired move duration in seconds
public float moveDuration = 1;
private bool isMoving;
privtae void Update()
{
if (!isMoving && Input.GetMouseButtonDown(0))
{
StartCoroutine(transform.position + Vector3.right * 1.5f, moveDuration);
}
if (!isMoving && Input.GetMouseButtonDown(1))
{
StartCoroutine(transform.position - Vector3.right * 1.5f, moveDuration);
}
}
private IEnumerator Move(Vector3 targetPosition, float duration)
{
if(isMoving) yield break;
isMoving = true;
var startPosition = transform.position;
var passedTime = 0f;
do
{
// This would move the object with a linear speed
var lerpfactor = passedTime / duration;
// This is a cool trick to simply ease-in and ease-out
// the move speed
var smoothLerpfactor = Mathf.SmoothStep(0, 1, lerpfactor);
transform.position = Vector3.Lerp(startPosition, targetPosition, smoothLerpfactor);
// increase the passedTime by the time
// that passed since the last frame
passedTime += Time.deltaTime;
// Return to the main thread, render this frame and go on
// from here in the next frame
yield return null;
} while (passedTime < duration);
// just like before set the target position just to avoid any
// under shooting
transform.position = targetPosition;
isMoving = false;
}
and you could still extend this example to also take the dtsnace to move into account like e.g.
var actualDuration = duration * Vector3.Distance(startPosition, targetPosition);
and then later everywhere use actualDuration.
Use transform.Translate instead:
public float moveSpeed = 3f;
void Update ()
{
//Moves Left and right along x Axis
transform.Translate(Vector3.right * Time.deltaTime * Input.GetAxis("Horizontal")* moveSpeed);
}
I am creating a game in Unity 2D. I have Dragons that I am adding to the scene. The dragons are only supposed to move in 1 of 4 directions, Up, Down, Left and Right. the dragons that move left and right move exactly as intended. However the dragons that move up and down have a problem in that they move at an angle. All dragons that move upwards move up and to the right at a 45 degree angle. All dragons that move downwards move down and to the left at a 45 degree angle.
at first I thought it was a problem with the animator moving the dragon to a different location, but I removed the animator component from the prefab and the problem still persisted.
below is the code I am using to move the dragons.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DragonMovment : MonoBehaviour {
public string Direction; //needs to be set in the prefab
public float DragonSpeed; //speed of dragon
Rigidbody2D rb;
public Transform Boundries;
// Use this for initialization
void Start ()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void FixedUpdate ()
{
float MoveRight = 1;
float MoveLeft = -1;
float MoveUp = 1;
float MoveDown = -1;
if (Direction== "R")
{
rb.velocity = new Vector3(DragonSpeed * MoveRight, rb.velocity.y);
}
if (Direction == "L")
{
rb.velocity = new Vector3(DragonSpeed * MoveLeft, rb.velocity.y);
}
if (Direction == "U")
{
rb.velocity = new Vector3(DragonSpeed * MoveUp, rb.velocity.x);
}
if (Direction == "D")
{
rb.velocity = new Vector3(DragonSpeed * MoveDown, rb.velocity.x);
}
}
}
Edit.
So why does the following work.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControler : MonoBehaviour {
// speed of movment
public float Speed;
// rb
Rigidbody2D rb;
public Transform Boundries;
// Use this for initialization
void Start () {
rb = GetComponent<Rigidbody2D>();
}
void FixedUpdate()
{
// get horizontal input
float MoveSide = Input.GetAxis("Horizontal");
//get vertical input
float MoveVert = Input.GetAxis("Vertical");
// move horizontal
rb.velocity = new Vector3(Speed * MoveVert, rb.velocity.y);
// move vertical
rb.velocity = new Vector3(Speed * MoveSide, rb.velocity.x);
}
}
but the other code doesent?
You have got the velocity x in the y of the vector 3
if (Direction == "U")
{
rb.velocity = new Vector3(rb.velocity.x, DragonSpeed * MoveUp);
}
if (Direction == "D")
{
rb.velocity = new Vector3(rb.velocity.x, DragonSpeed * MoveDown);
}
It works in your player script as you are overriding the values in the subsequent statement.
float MoveSide = Input.GetAxis("Horizontal"); //eg 1
float MoveVert = Input.GetAxis("Vertical"); // eg 1
// setting your x velocity incorrectly to the y (vert) velocity speed and keeping y the same velocity as start of frame
rb.velocity = new Vector3(Speed * MoveVert, rb.velocity.y);
// Set the y to the x value of the statement above so it is now in the correct vector and set the x to the correct hoz velocity
rb.velocity = new Vector3(Speed * MoveSide, rb.velocity.x);
// effectively doing
rb.velocity = new Vector3(Speed * MoveSide, Speed * MoveVert);
You should also be using MovePosition as it doesn't directly affect the physics engine (using velocity can have knock on effects to collisions and triggers and create unexpected physics). Your gameobjects will have to be marked as kinematic otherwise the below will cause them to teleport to the new position instantly.
var movementDirection = new Vector3(Speed * MoveSide, Speed * MoveVert);
rb.MovePosition(transform.position + movementDirection * Time.deltaTime);
And the * Time.deltaTime ensures that movement is consistent for different framerates. If you run the game on a 30 fps machine the game objects will move slower than a 60fps. Time.deltaTime calculates the physical time passed since the previous frame and ensures the distance traveled is the same regardless of frame rate.
e.g say the gameObject moves 1 per frame update. After a second on a 30 fps machine the object would have moved 30. After a second on a 60 fps machine the object would have moved 60.
Time.deltaTime=.2s on 30 fps so 1 movement * .2 = move .2 per frame * 30 frames in the second = 60 moved
Time.deltaTime=.1s on 60 fps so 1 movement * .1 = move .1 per frame * 60 frames in the second = 60 moved
I'm doing a 2D platform game where the player only climbs (y-axis).
What I'm trying to do is when I reach near to the top edge, my camera goes up a little bit so I can see more of the level.
I have this, but it's not working:
Public Transform player;
Void Start() { }
Void Update()
{
if (player.transform.position > Screen.height - 50)
{
transform.position.y += speed * Time.deltaTime;
}
}
Other Way is like this But not working more than once, i might need to set move = false; but dont know how without interupting Lerp:
float moveTime = 1f; // In seconds
float moveTimer = 0.0f; // Used for keeping track of time
bool moving = false; // Flag letting us know if we're moving
public float heightChange = 7.0f; // This is the delta
// These will be assigned when a collision occurs
Vector3 target; // our target position
Vector3 startPos; // our starting position
void Start()
{
}
void Update()
{
// If we're currently moving and the movement hasn't finished
if (moving && moveTimer < moveTime)
{
// Accumulate the frame time, making the timer tick up
moveTimer += Time.deltaTime;
// calculate our ratio ("t")
float t = moveTimer / moveTime;
transform.position = Vector3.Lerp(startPos, target, t);
}
else
{
// We either haven't started moving, or have finished moving
}
}
void OnTriggerEnter2D(Collider2D other)
{
if (!moving)
{
// We set the target to be ten units above our current position
target = transform.position + Vector3.up * heightChange;
// And save our start position (because our actual position will be changing)
startPos = transform.position;
// Set the flag so that the movement starts
moving = true;
}
}
}
you are comparing two different things here. Screen.height is in pixels while player position is in world units. so lets assume your screen is 1024x768 you are checking if your player is above 718 which is a huge amount in world units.
What you have to do is convert one to the others unit and compare then for example with http://docs.unity3d.com/ScriptReference/Camera.ScreenToWorldPoint.html
Another thing i noticed is that this script is named player so i assume this is controlling your player object in which case
transform.position.y += speed * Time.deltaTime;
would only change your players position. To change the position of the main camera you could use:
Camera.main.transform.position += new Vector3(0f, speed * Time.deltaTime, 0f);
And lastly, always post the errors you get when asking questions, explain what you expect to happen and what actually happens.
You could try lerping the between positions eg.
transform.position = new trasform.lerp(transform.position, player.transform.position, speed*Time.DeltaTime);
just use the if statement to trigger a bool that then does the lerp, so the camera will move to what you need it to and not just when the player hits a certain point. Then when the camera is finished mooving, reset the bool ready for next time