I'm new to Unity and C#, but I'm trying to learn by making a basic RPG. I've gotten to a point, where my player character can move, but when I try to make an NPC walk to a specific point, it just teleports instantly.
using UnityEngine;
public class Walker : MonoBehaviour
{
public float walkerSpeed = 1.0f;
public void moveWalker(float x, float y)
{
Vector2 movement = new Vector2(x, y) * walkerSpeed;
movement *= Time.fixedDeltaTime;
transform.Translate(movement);
}
public void ScriptedWalk(float MoveToX, float MoveToY)
{
while (Mathf.Abs(MoveToX - transform.position.x) > 0.05f)
if (MoveToX > transform.position.x)
moveWalker(1, 0);
else
moveWalker(-1, 0);
}
}
I'm calling ScriptedWalk from my NPC's Start function, and I got stuck testing movement on the x axis. The NPC is in X = -2.5 and neds to get to X = 2.
What am I not understanding here?
Thank you Douglas, as you've suggested in your comment, I used a Coroutine, and changed my ScriptedWalk to this:
public IEnumerator ScriptedWalk(float MoveToX, float MoveToY)
{
if (Mathf.Abs(MoveToX - transform.position.x) < 0.05f)
{
// set animator floats to 0
stopWalker();
}
else
{
// Move east
if (MoveToX > transform.position.x)
moveWalker(1, 0);
// Move west
else
moveWalker(-1, 0);
}
yield return null;
}
I am also calling it from my FixedUpdate method.
Related
So I'm Trying To Rotate My Coin Object to 0z if Y is above 0.17f and if its not will rotate back to 90z , the first if statement works fine but the other one keeps rotating my coin and i don't know why...? I'm Completely New To Unity and C# Lang !
Anyway Here's my code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CoinScript : MonoBehaviour
{
public GameObject coin;
bool isRotated = false;
// Update is called once per frame
void FixedUpdate()
{
if (coin.transform.position.y > 0.17f && coin.transform.rotation.z >= 0f && !isRotated)
{
coin.transform.Rotate(coin.transform.rotation.x,coin.transform.rotation.y,-1f );
if (coin.transform.rotation.z <= 0f)
{
isRotated = true;
}
}else if (coin.transform.position.y < 0.17f && coin.transform.rotation.z <= 90f && isRotated)
{
coin.transform.Rotate(coin.transform.rotation.x,coin.transform.rotation.y,1f);
if (coin.transform.rotation.z >= 90f)
{
isRotated = false;
}
}
}
}
Transform.rotation is a Quaternion!
As the name suggests a Quaternion (also see Wikipedia -> Quaternion has not three but four components x, y, z and w. All these move within the range -1 to +1. Unless you really know exactly what you are doing you never touch these components directly.
If I understand you correctly you rather want to do something like e.g.
public class CoinScript : MonoBehaviour
{
public Transform coin;
public float anglePerSecond = 90;
private void FixedUpdate()
{
var targetRotation = coin.position.y > 0.17f ? Quaternion.identity : Quaternion.Euler(0, 0, 90);
coin.rotation = Quaternion.RotateTowards(coin.rotation, targetRotation, anglePerSecond * Time.deltaTime);
}
}
This is a pretty strange bug. I created the following script intended to calculate positions to shoot projectiles in my game by moving around a circular path and facing the mouse:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProjectileFire : MonoBehaviour
{
[System.Serializable]
public class ButtonDirectionSet {
public string button;
public float direction;
public bool degrees = true;
}
public GameObject projectile;
public Vector2 centerPoint;
public float radius = 1.0f;
public string fireButton = "Fire1";
public bool followMouse = false;
public bool followKeyboard = true;
public List<ButtonDirectionSet> buttonDirectionMap = new List<ButtonDirectionSet>();
public bool degrees = true;
public float direction;
private readonly float maxDirection = 360 * Mathf.Deg2Rad;
// Start is called before the first frame update
void Start()
{
foreach (ButtonDirectionSet set in buttonDirectionMap) {
if (set.degrees) {
set.direction *= Mathf.Deg2Rad;
}
}
if (degrees) {
direction *= Mathf.Deg2Rad;
}
}
void Update()
{
foreach (ButtonDirectionSet set in buttonDirectionMap) {
if (Input.GetButtonDown(set.button)) {
direction = set.direction;
}
}
if ((followKeyboard && followMouse && Input.GetMouseButton(0)) || !followKeyboard) {
Vector3 mouse = Input.mousePosition;
mouse.z = transform.position.z - Camera.main.transform.position.z;
Vector3 mousePosition = transform.InverseTransformPoint(Camera.main.ScreenToWorldPoint(mouse));
float xPos = mousePosition.x - centerPoint.x;
if (Mathf.Abs(xPos) < 0.1f)
{
xPos = 0.1f * Mathf.Sign(xPos);
}
float yPos = mousePosition.y - centerPoint.y;
if (Mathf.Abs(yPos) < 0.1f)
{
yPos = 0.1f * Mathf.Sign(yPos);
}
direction = Mathf.Atan2(yPos, xPos);
}
if (direction < 0) direction += maxDirection;
transform.localPosition = centerPoint + (new Vector2(Mathf.Cos(direction), Mathf.Sin(direction)) * radius);
}
}
If you set both "follow keyboard" and "follow mouse" to true, the object should be positioned toward the mouse when you hold down the left click button. For the most part, this works, but for some reason, there's jitter in the sense that the object rapidly changes between two different positions for a period of time, even when I don't move the mouse at all. Additionally, the "direction" value switches itself at the same rate, even if I don't move my mouse at all. This does not happen all the time, but it does happen pretty frequently at times that appear to be random.
Is there anything I can do to mitigate or eliminate this behavior?
Since the direction also jitters, there is something wrong with the way you calculate the direction.
The reason your direction jitters is because of the transform.InverseTransformPoint. Since the position of the object changed, the position of the mouse relative to the object also changed with every movement so the direction also changed.
Here are two ways to do it:
1:
Vector2 direction;
if ((followKeyboard && followMouse && Input.GetMouseButton(0)) || !followKeyboard)
{
Vector3 mouseOnScreen = Camera.main.ScreenToWorldPoint(Input.mousePosition) - new Vector3(centerPoint.x, centerPoint.y, 0);
direction = new Vector2(mouseOnScreen.x, mouseOnScreen.y);
}
transform.localPosition = centerPoint + (direction.normalized * radius);
This is a simple way to do it without using angles. It gets the position of the mouse on screen in world space. Then it makes it into a vector 2 so the z value doesn't affect it. Then, it sets the new position to the center plus the direction to the mouse with a magnitude of the radius
2:
if ((followKeyboard && followMouse && Input.GetMouseButton(0)) || !followKeyboard)
{
Vector3 mousePosition = Camera.main.ScreenToWorldPoint(Input.mousePosition) - new Vector3(centerPoint.x, centerPoint.y, 0);
direction = Mathf.Atan2(mousePosition.y, mousePosition.x);
}
transform.localPosition = centerPoint + (new Vector2(Mathf.Cos(direction), Mathf.Sin(direction)) * radius);
This is the same way you did it but a little cleaned up. Instead of using transform.InverseTransformPoint, this way subtracts the centerPoint. That returns the vector from the centerPoint to the mouse, setting the centerPoint as the origin. This is also in the firsts solution.
I made a Camera Script that follows the player using a lerp for making it look more smooth, but for some reason, it looks laggy when walking in some sudden areas.
The ground the player is walking on is tilemap, and at first, I thought there were tiny cell gaps that caused the problem, but it still happens even when I changed the ground to one solid block. So I concluded it must be something with Camera Script that cause this problem.
Here is a video clip of what I'm talking about: https://youtu.be/mmBMHWuHpxo
I think the problem stems from this part of my camera script:
void SetTargetPos()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
targetX = transform.position.x;
targetY = transform.position.y;
// If the player has moved beyond the x margin...
if (CheckXMargin())
{
// ... the target x coordinate should be a Lerp between the camera's current x position and the player's current x position.
targetX = Mathf.Lerp(transform.position.x, transformPlayer.position.x, xSmooth * Time.deltaTime);
}
// If the player has moved beyond the y margin...
if (CheckYMargin())
{
// ... the target y coordinate should be a Lerp between the camera's current y position and the player's current y position.
targetY = Mathf.Lerp(transform.position.y, transformPlayer.position.y, ySmooth * Time.deltaTime);
}
// The target x and y coordinates should not be larger than the maximum or smaller than the minimum.
targetX = Mathf.Clamp(targetX, currentMinBounds.x, currentMaxBounds.x);
targetY = Mathf.Clamp(targetY, currentMinBounds.y, currentMaxBounds.y);
// Set the camera's position to the target position with the same z component.
transform.position = new Vector3(targetX, targetY, transform.position.z);
TestOutOfCamBounds();
}
Btw I have the exact same script on different Unity Projects with the same variable inputs. There is a small difference, and that just the overall size of everything in the second project is a lot smaller than the first one. But it works completely fine on that project. I have also tried Smoothdamp instead of Lerp, still no success. Here is a video clip of the other project: https://youtu.be/baJmKehYfG0
Any help will be much appreciated.
If you want to look at the entire script here it is:
public class Camera_Controller : MonoBehaviour
{
private static Camera_Controller _instance;
public static Camera_Controller instance;
[Header("Put player here")]
public GameObject player;
public Transform transformPlayer;
[Space]
Vector2 currentMinBounds;
Vector2 currentMaxBounds;
public Vector3 targetPos;
[Space]
[Header("Camera Properties")]
public float xMargin = 1f;
public float yMargin = 1f;
public float xSmooth = 8f;
public float ySmooth = 8f;
public Vector3 velocity = Vector3.zero;
private float targetY;
private float targetX;
[Header("Smooth Transition")]
public Vector3 oldPosition;
public Vector3 targetPosition;
public float transitionsTime;
public bool switchingCamera;
private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(gameObject);
return;
}
else
{
_instance = this;
}
instance = _instance;
}
void Start()
{
targetPos = transform.position;
}
private bool CheckXMargin()
{
// Returns true if the distance between the camera and the player in the x axis is greater than the x margin.
return Mathf.Abs(transform.position.x - transformPlayer.position.x) > xMargin;
}
private bool CheckYMargin()
{
// Returns true if the distance between the camera and the player in the y axis is greater than the y margin.
return Mathf.Abs(transform.position.y - transformPlayer.position.y) > yMargin;
}
void Update()
{
if (!switchingCamera)
{
SetTargetPos();
}
}
public void SetCamBounds(Vector2 minBounds, Vector2 maxBounds) //Called from Game Events Trough WarpController as trigger
{
currentMinBounds = minBounds;
currentMaxBounds = maxBounds;
}
//SetTargetPos() should be causing the problem
void SetTargetPos()
{
// By default the target x and y coordinates of the camera are it's current x and y coordinates.
targetX = transform.position.x;
targetY = transform.position.y;
// If the player has moved beyond the x margin...
if (CheckXMargin())
{
// ... the target x coordinate should be a Lerp between the camera's current x position and the player's current x position.
targetX = Mathf.Lerp(transform.position.x, transformPlayer.position.x, xSmooth * Time.deltaTime);
}
// If the player has moved beyond the y margin...
if (CheckYMargin())
{
// ... the target y coordinate should be a Lerp between the camera's current y position and the player's current y position.
targetY = Mathf.Lerp(transform.position.y, transformPlayer.position.y, ySmooth * Time.deltaTime);
}
// The target x and y coordinates should not be larger than the maximum or smaller than the minimum.
targetX = Mathf.Clamp(targetX, currentMinBounds.x, currentMaxBounds.x);
targetY = Mathf.Clamp(targetY, currentMinBounds.y, currentMaxBounds.y);
// Set the camera's position to the target position with the same z component.
transform.position = new Vector3(targetX, targetY, transform.position.z);
TestOutOfCamBounds();
}
void TestOutOfCamBounds() //Set Camera some boundaries.
{
if (targetPos.x <= currentMinBounds.x)
{
targetPos.x = currentMinBounds.x;
}
if (targetPos.x >= currentMaxBounds.x)
{
targetPos.x = currentMaxBounds.x;
}
if (targetPos.y <= currentMinBounds.y)
{
targetPos.y = currentMinBounds.y;
}
if (targetPos.y >= currentMaxBounds.y)
{
targetPos.y = currentMaxBounds.y;
}
}
}
Try doing the call for the cammer movement in Late update, Example bellow.
private void LateUpdate() {
SetTargetPos();
}
This is called after the update method and might help reduce the cammer jitter.
Your second video is 2d and your first is 3d. try lerping the z position also.
targetZ = Mathf.Lerp(transform.position.z, transformPlayer.position.z, zSmooth * Time.deltaTime);
I found a solution to it all!
I changed the update function to LateUpdate()... Sorry for wasting your time
I have a problem with turning my avatar at a certain position on the map. The character moves back and forth, but he doesn't want to rotate at coordinates i entered. What am i doing wrong?
this is my code:
sing System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AvatarPingPong : MonoBehaviour {
// Use this for initialization
public float speed;
public float startXCord, endXCord;
float endTrunx, startTurnx;
public GameObject obj;
Vector3 EndPoint, StartPoint;
void Start () {
EndPoint = transform.position;
endXCord = EndPoint.x;
endTrunx = EndPoint.x - 2f;
StartPoint = transform.position;
StartPoint.x = startXCord;
startTurnx = StartPoint.x + 2f;
}
// Update is called once per frame
void Update () {
transform.position = new Vector3(PingPong (Time.time * speed, startXCord, endXCord ), transform.position.y, transform.position.z);
if (transform.position.x == startTurnx ) {
Debug.Log("start Check");
obj.transform.Rotate(0f, 180f, 0f);
}
if (transform.position.x == endTrunx ) {
obj.transform.Rotate(0f, 180f, 0f);
Debug.Log("einde check");
}
}
//function to change the default starting value of (0, 0, 0) to any value
float PingPong(float t, float minLength, float maxLength) {
return Mathf.PingPong(t, maxLength-minLength) + minLength;
}
}
I believe the problem is that you're trying to flip your avatar once he reaches a particular x-coordinate, but he may never reach that EXACT coordinate. if (transform.position.x == startTurnx) will only return true if the two values are EXACTLY the same, and your avatar isn't actually moving smoothly across the screen. He's actually jumping minute amounts every frame, so he may never land on exactly that point.
Instead, my recommendation would be to compare his new position against his old position to see which direction he's traveling in and flip him when he changes direction. Some code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AvatarPingPong : MonoBehaviour {
// Use this for initialization
public float speed;
public float startXCord, endXCord;
float endTrunx, startTurnx;
public GameObject obj;
Vector3 EndPoint, StartPoint;
//I'm going to assume you start it moving left. You may have to change this
bool goingLeft = false;
void Start () {
EndPoint = transform.position;
endXCord = EndPoint.x;
endTrunx = EndPoint.x - 2f;
StartPoint = transform.position;
StartPoint.x = startXCord;
startTurnx = StartPoint.x + 2f;
}
// Update is called once per frame
void Update () {
float prevX = transform.position.x;
float newX = PingPong (Time.time * speed, startXCord, endXCord );
transform.position = new Vector3(newX, transform.position.y, transform.position.z);
if (newX > prevX) {
//avatar is moving to the right, check to see if that's the direction it was going last Update
if (goingLeft) {
Debug.Log("Flipping Right");
obj.transform.Rotate(0f, 180f, 0f);
goingLeft = false;
}
}else if (newX < prevX){
//avatar is moving to the left, check to see it that's the direction it was going last Update
if (!goingLeft) {
Debug.Log("Flipping Left");
obj.transform.Rotate(0f, 180f, 0f);
goingLeft = true;
}
}
}
//function to change the default starting value of (0, 0, 0) to any value
float PingPong(float t, float minLength, float maxLength) {
return Mathf.PingPong(t, maxLength-minLength) + minLength;
}
}
I am trying to achieve basic racing game. Infinite racing game, movement method like a subway surfers. I have a problem about changing lane. I dont want to teleport to other lane, I want to smoothly. I am newbee in unity, I have try Lerp method but it is not working.
using UnityEngine;
using System.Collections;
public class VehicleController : MonoBehaviour
{
public float drift;
public Vector3 positionA;
public Vector3 positionB;
public Vector3 positionC;
public Vector3 positionD;
private Transform tf;
private Rigidbody rb;
private Vector3 vehiclePos;
void Awake()
{
//rb = GetComponent<Rigidbody> ();
tf = transform;
}
void Update()
{
vehiclePos = tf.position;
if (Input.GetKey( KeyCode.Space ))
DecreaseSpeed ();
else
IncreaseSpeed ();
if (Input.GetKeyDown (KeyCode.A))
{
MoveToRight ();
Debug.Log( "Move to Right!" );
}
if (Input.GetKeyDown (KeyCode.D))
{
MoveToLeft ();
Debug.Log( "Move to Left!" );
}
}
void FixedUpdate()
{
tf.Translate (Vector3.forward * speed * Time.deltaTime);//My Movement Method.
}
void MoveToLeft()
{
if (vehiclePos.position.x == positionA.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionB, Time.deltaTime * drift);
}
void MoveToRight()
{
if (vehiclePos.position.x == positionB.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionA, Time.deltaTime * drift);
}
}
First: Don't use == for position.x, since it's a floating-point (decimals) value and in this case it would be very rare for it to return "true". Here's some info about comparing floats.
Second: It doesn't look like you're connecting your actual position with vehiclePos anywhere. transform.position is what you want there.
Third: Input.GetAxis() is a cleaner way to deal with direction input. Instead of specifically calling out each button you can deal with just one float value between -1 and 1. It will also let you reconfigure the keys easily.
Fourth: In an infinite runner it is better to have the world move towards your character and camera than to have the character and camera actually move forward. Floating point numbers get less precise as you move further away from zero, so you should have your action take place relatively close to the world origin (0,0,0) point if you can.
If you want to press the button once to change lanes, you should keep an integer variable that saves which lane you're currently in. If you press LEFT you subtract one, and if you press RIGHT you add one. You should also add a check to make sure it stays within the desired range.
Then in Update() you just need to ALWAYS Lerp towards that X value. You can use Mathf.Lerp to lerp only one variable at a time if you want.
public int laneNumber = 0;
public int lanesCount = 4;
bool didChangeLastFrame = false;
public float laneDistance = 2;
public float firstLaneXPos = 0;
public float deadZone = 0.1f;
public float sideSpeed = 5;
void Update() {
//"Horizontal" is a default input axis set to arrow keys and A/D
//We want to check whether it is less than the deadZone instead of whether it's equal to zero
float input = Input.GetAxis("Horizontal");
if(Mathf.Abs(input) > deadZone) {
if(!didChangeLastFrame) {
didChangeLastFrame = true; //Prevent overshooting lanes
laneNumber += Mathf.roundToInt(Mathf.Sign(input));
if(laneNumber < 0) laneNumber = 0;
else if(laneNumber >= lanesCount) laneNumber = lanesCount - 1;
}
} else {
didChangeLastFrame = false;
//The user hasn't pressed a direction this frame, so allow changing directions next frame.
}
Vector3 pos = transform.position;
pos.x = Mathf.Lerp(pos.x, firstLandXPos + laneDistance * laneNumber, Time.deltaTime * sideSpeed);
transform.position = pos;
}
You could likely just use this code as-is, but I suggest you look it over and try to figure out an understanding of how and why it works. A newbie today who always seeks to improve their skill can do something amazing next week. Hope this helps. :)