unity update skipping calculation in a frame - unity3d

I'm Instantiating tiles as a path, on every four instantiating's tiles, they should start instantiating towards right, after four instantiating's, they go straight again.
to check this logic I, made a variable spawnOffset and incrementing +1 each frame.
if spawnOffset % 4 == 0 change direction
but, i'm not getting a change in direction in regular intervals, when i debug, the frame skips and so is the logic
public GameObject go;
public Transform Playertransform;
public Vector3 tileSpawnOffset = Vector3.zero;
private Vector3 direction = Vector3.forward;
public int SpawnOffset = -3;
private bool turnRight = false;
void Update()
{
SpawnPath();
ChangeDirection();
}
void SpawnPath()
{
if (Playertransform.position.z > SpawnOffset)
{
tileSpawnOffset += direction;
Instantiate(go, this.transform.position + tileSpawnOffset, this.transform.rotation, this.transform);
SpawnOffset++;
}
}
void ChangeDirection()
{
if (SpawnOffset % 4 == 0)
{
turnRight = !turnRight;
}
direction = turnRight == true ? Vector3.right : Vector3.forward;
}
and but when i tried with time instead
float time = 0f;
void ChangeDirection()
{
time += Time.deltaTime;
if (time > 1)
{
turnRight = !turnRight;
time = 0;
}
direction = turnRight == true ? Vector3.right : Vector3.forward;
}
it works perfectly fine. so, how could i fix it. I, don't want use time, i want to change direction exactly after 4 tiles spawned

Since your always calling both SpawnPath(); ChangeDirection(); everyframe if (Playertransform.position.z > SpawnOffset) is ever false twice in a row your turnright bool gets flipped regardless every frame your on a multiple of 4. You don't need to call ChangeDirection if you didn't spawn a new tile. if you just remove that call from update and add it at right after your increment SpawnOffset its probably going to fixe it.

Related

Unity RotateAround Clamp Dead Zone Issue

For the past 3 days, I have tried numerous methods and read dozens of questions on forums in order to achieve the desired effect of rotating a camera on the Y axis using mouse rotations, and clamping the rotation within a certain range. The amount of rotation is dependent on how close the mouse is to the edge of the screen.
I was unable to find a solution, but I was able to learn enough to have my own honest attempt at it, and I came quite close. The prominent issue I faced was being able to properly clamp the RotateAround() inside of the desired range. This is because of eulerAngles being 0 - 360, and having the min and max of the clamp transition to the other side of the spectrum.
By clamping the rotation degrees before calling RotateAround, I was able to get the clamping to work, though I had to use Booleans to get them to work properly.
My current issue is that when the initial Y rotation(anchor) is slightly below 360, the camera fails to clamp on the left and will clamp back around to the right side of the range.
In all other situations, the clamping works just fine:
Initial Y rotation slightly above 0
Not within the dead zone
While debugging, I find that this is because rightOverFlag is true. When I set this bool to true manually in situations clamping normally works fine, the clamping will no longer work on the left.
I can't seem to find how this bool being true causes something like this to happen, so I am hoping some fresh eyes and seasoned advice can help me learn from this. Thank you.
public gameState gameState;
public openCams openCams;
private GameObject GameStateManager;
[SerializeField]
private GameObject Player;
[SerializeField]
private Camera cam;
// Rotation Variables
private Quaternion initialRotation;
private float initialYRotation = 0f;
[SerializeField]
private float angleRange;
private float anchorY = 0f;
public bool leftOverFlag = false;
public bool rightOverFlag = false;
void Start()
{
cam = Camera.main;
Cursor.lockState = CursorLockMode.None;
}
public void OrientControls()
{
initialRotation = Player.transform.rotation;
initialYRotation = Player.transform.eulerAngles.y;
anchorY = initialYRotation;
if (anchorY > 360)
{
anchorY -= 360;
}
rightOverFlag = false;
leftOverFlag = false;
if ((anchorY + angleRange) > 360)
{
rightOverFlag = true;
}
else if ((anchorY - angleRange) <= 0)
{
leftOverFlag = true;
}
}
void Update()
{
if (openCams.GetMonitorState() == false)
{
mousePos = Input.mousePosition;
float rotateDegrees = 0f;
//Debug.Log(Player.transform.eulerAngles.y);
// THERE IS CODE HERE THAT INCREASES ROTATE DEGREES BASED ON MOUSE POSITION. I removed it for the sake of readability, because it was quite long and unrelated to my issue.
float angleFromInitial = Quaternion.Angle(initialRotation, Player.transform.rotation);
float currentPlayerYRot = Player.transform.eulerAngles.y;
if (currentPlayerYRot > 360)
{
currentPlayerYRot -= 360;
}
bool currentRightOverageFlag = false;
bool currentLeftOverageFlag = false;
if (rightOverFlag)
{
if ((currentPlayerYRot) < (anchorY - (angleRange - 5))) //
{
currentRightOverageFlag = true;
}
}
if (leftOverFlag)
{
if ((currentPlayerYRot) > (anchorY + (angleRange + 5)))
{
currentLeftOverageFlag = true;
}
}
// !!! - For some reason, when rightOverFlag is enabled, the clamp does not work on the left side. In all other situations, the clamp works perfectly.- !!!
if (!currentLeftOverageFlag && !currentRightOverageFlag)
{
if (currentPlayerYRot < anchorY) // Regular
{
angleFromInitial *= -1;
}
}
else if (currentLeftOverageFlag && !currentRightOverageFlag)
{
if (currentPlayerYRot > anchorY) // If over the left line
{
angleFromInitial *= -1;
}
}
else if (!currentLeftOverageFlag && currentRightOverageFlag)
{
if (currentPlayerYRot > anchorY) // If over the right line
{
angleFromInitial *= -1;
}
}
else
{
Debug.Log("staticPersonController: ERROR => Cannot have current left and right overage flags enabled at the same time.");
}
currentLeftOverageFlag = false;
currentRightOverageFlag = false;
float newAngle = Mathf.Clamp(angleFromInitial + rotateDegrees, -angleRange, angleRange);
rotateDegrees = newAngle - angleFromInitial;
Player.transform.RotateAround(Player.transform.position, Vector3.up, rotateDegrees);
}
}
}
Assume you have these two variables set.
// This is the initial value of the player's Y rotation. Set only once.
float initialRotY = Player.transform.eulerAngles.y;
// This is the offset of the player's Y rotation based on the cursor position
// e.g. -90 when the cursor is on the left edge and 90 on the right
float mouseBasedRotY;
In the Update method, just do this.
Player.transform.rotation = Quaternion.Euler(0f, initialRotY + mouseBasedRotY, 0f);
You don't have to clamp the value, because the degree is always limited in [initialRotY-90, initialRotY+90]

How to use Lerp Dash Movement in Unity

I'm doing a beginner project in Unity and want to use Vector3.Lerp to do animation for the dash movement. Here is my code and I'm struggling to see why it doesn't work.
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
Vector3 start = transform.position;
Vector3 dash = new Vector3(start.x + m_DashDist, start.y);
transform.position = Vector3.Lerp(start, dash, Time.deltaTime);
}
}
How Lerp Works
The last argument you pass to the Lerp function is a float value (mostly between 0f and 1f).
If this value is 0f, Lerp will return the first argument (let's say _dashStart).
If this value is 1f, Lerp will return the second argument (let's say _dashEnd).
If this value is 0.5f, Lerp will return the "mid" between the first and the second argument.
As you can see, this function interpolates between these two arguments depending on the third argument.
This function has to be called every frame and the float value (the 3rd argument) needs to be incremented by Time.deltaTime every frame in your case.
But your if block is only executed once, when you press E. For instance, you can set a bool to true when the E key has been pressed. Here is the further code – hopefully with no mistakes:
public float dashTime = 0.1f;
private float _currentDashTime = 0f;
private bool _isDashing = false;
private Vector3 _dashStart, _dashEnd;
void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
if (_isDashing == false)
{
// dash starts
_isDashing = true;
_currentDashTime = 0;
_dashStart = transform.position;
_dashEnd = new Vector3(_dashStart.x + m_DashDist, _dashStart.y);
}
}
if (_isDashing)
{
// incrementing time
_currentDashTime += Time.deltaTime;
// a value between 0 and 1
float perc = Mathf.Clamp01(_currentDashTime / dashTime);
// updating position
transform.position = Vector3.Lerp(_dashStart, _dashEnd, perc);
if (_currentDashTime >= dashTime)
{
// dash finished
_isDashing = false;
transform.position = _dashEnd;
}
}
}
There are different ways of using Lerp. I mostly use this approach.

Unity3d lift movement down with keycode

this code does so when I press the A key or the B key, the elevator stops upwards on the first or second floor.
The question is
How do I modify the code that when the elevator is on the second floor, for example, and I press the A key again to make the elevator go down to the first floor?
Question two
If I replace
if (Input.GetKey (KeyCode.A)) {}
with this
if (Input.GetKeyUp (KeyCode.Keypad1)) {}
so the code does not work. Why?
Thank you for advice
Sorry for the bad English. Here is the code
public GameObject lift;
private bool keyHHit=false;
private bool keyHHitB=false;
void Update ()
{
if (Input.GetKey(KeyCode.B))
{
keyHHit=true;
}
if( keyHHit==true)
{
if(transform.localPosition.y >= 14.52)
{
transform.Translate(new Vector3(0, 0, 0) * 2 * Time.deltaTime, Space.Self);
}
else
{
transform.Translate(new Vector3(0, 2, 0) * 2 * Time.deltaTime, Space.Self);
}
}
if (Input.GetKey(KeyCode.A))
{
keyHHitB=true;
}
if( keyHHitB==true)
{
if(transform.localPosition.y >= 8.52)
{
transform.Translate(new Vector3(0, 0, 0) * 2 * Time.deltaTime, Space.Self);
}
else
{
transform.Translate(new Vector3(0, 2, 0) * 2 * Time.deltaTime, Space.Self);
//transform.Translate(Vector3.up * 0.05f);
}
}
}
So if I understand you correctly
you have two or 3 floors (doesn't matter, could be more later).
Once you press a button you want to move towards the target floor until you reach there. "Lock" input meanwhile
Once you reached a floor "unlock" Input again and allow to also go down if the current floor is above the target floor
I would use a Coroutine and Vector3.Lerp for this.
First lets have a class for floors so it is easier to add additional floors later
[Serializable]
public class FloorSetting
{
public KeyCode Key;
public Vector3 Position;
}
Then simply have an array of all floors target positions and according keys
// Configure these floors in the Inspector
// simply add the entries you need and adjust buttons and target positions
[SerializeField] private FloorSetting[] floors;
[SerializeField] private float moveUnitsPerSecond = 1f;
// This flag is for blocking any Input while moving
// thus preventing concurrent routines
private bool isMoving;
private void Update()
{
// if already moving do nothing
if(isMoving) return;
// check if any of the configured keys was pressed and start moving
// towards the according position
foreach(var floor in floors)
{
// You want to use GetKeyDown to only get the first press event
// you don't care about continues press since you will keep moving automatically until reaching the floor
if(Input.GetKeyDown(floor.Key))
{
StartCoroutine(MoveRoutine(floor.Position));
}
}
}
private IEnumerator MoveRoutine(Vector3 targetPosition)
{
// block concurrent routine
if(isMoving) yield break;
isMoving = true;
// Get the duration of movement
var startPosition = transform.position;
var duration = Vector3.Distance(startPosition, targetPosition) / moveUnitsPerSecond;
// Move smoothly towards the target position and add some easing
var timePassed = 0f;
while(timePassed <= duration)
{
// A interpolation factor between 0 and 1
var factor = timePassed / duration;
// optionally add ease-in and out
factor = Mathf.SmoothStep(0, 1, factor);
// Set the position to an interpolated position between start and target depending on the factor
transform.position = Vector3.Lerp(startPosition, targetPosition, factor);
// increase by the time passed since last frame
timePassed += Time.deltaTime;
// Tells Unity to "pause" the routine here, render this frame
// and continue from here in the next frame
yield return null;
}
// Just to be sure in he end set it to hard position
transform.position = targetPosition;
// optionally add some cooldown in seconds
yield return new WaitForSeconds(1f);
// Release the lock so next move can start now
isMoving = false;
}
If you don't need the easing you could as well also implement it lot easier using Vector3.MoveTowards like e.g.
while(!Mathf.Approximately(Vector3.Distance(transform.position, targetPosition), 0)
{
transform.position = Vector3.MoveTowards(transform.position, targetPosition, moveUnitsPerSecond);
yield return null;
}
transform.position = targetPosition;
Note: Typed on smartphone but I hope the idea gets clear

Unity 2D Rigidbody.velocity and transform.position causes random 'ghost' frame

I am creating a 2D platform / adventure game that uses flip screen to manage changing rooms.
My character moves using RigidBody.velocity, this works as I need it to, when my character reaches one of the screen's left or right extremes I use transform.position to move (effectively teleporting) the character to the other side of the screen, I then change the room. This gives the effect of moving to the next room along.
My flip screen code works fine, the changing room effect works without a problem, in fact almost everything works just as I like it, except that I get 1 frame where the character appears at a random point in between where the character moved from and where he moved to.
For example, my room is 16 tiles across, if my character moves left and passes x position '0' (the very left of the screen) I make a transform.position to x position 16 (keeping the y position constant) and then change rooms. I have logged the movement and what happens is (pseudo code)..
x < 0 (the change room code is run)
translate.position to x=16 (the far right)
x = 16 (this is correct)
The room changes
x = some float somewhere between 0 and 16 (this is NOT correct)
x = 16 (back to the correct position again)
This is my player code:
using UnityEngine;
using System.Collections;
public class MKManager : MonoBehaviour {
public float maxSpeed = 0.10f;
bool isFacingRight = false;
Rigidbody2D myBody;
bool grounded = true;
public static int liftdirection = 0;
void Start (){
myBody = GetComponent<Rigidbody2D> ();
}
void Update () {
checkRoomBounds ();
}
void FixedUpdate(){
float h = Input.GetAxisRaw ("Horizontal");
if (grounded) {
myBody.velocity = new Vector2 (h * maxSpeed, myBody.velocity.y);
if (h < 0) { //Moving Left
if (isFacingRight) {
FlipAnimation ();
}
} else if (h > 0) { //Moving Right
if (!isFacingRight) {
FlipAnimation ();
}
}
}
}
void checkRoomBounds(){
if (transform.position.x < 0) {
transform.position = new Vector3 (16.0f, transform.position.y, 0.0f);
GameManager.instance.moveRoom (-1);
} else if (transform.position.x > 16) {
transform.position = new Vector3 (0.0f, transform.position.y, 0.0f);
GameManager.instance.moveRoom (1);
}
}
void FlipAnimation(){
isFacingRight = !isFacingRight;
Vector3 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
I have tried calling checkRoomBounds() in update, fixed update and lateupdate, all give the same result. If I do not call the moveRoom() function the same happens but the room is not changed, but the moveRoom() code is...
public void moveRoom(int direction){
if (room == 0) {
room = (10*currentFloor) + 1;
} else {
room += direction;
if (room == -1) {
room = 68;
} else if (room == 69) {
room = 0;
} else if (room % 10 == 9 || room % 10 == 0) {
//room += (direction * 2);
room = 0;
}
}
boardScript.SetupScene (room);
}
I've run out of ideas and don't know where I'm going wrong. Any help would be much appreciated.
Many thanks in advance

Lifting platforms is not working as it should

I would like to make some lifting platforms in my game, so if the platform went down, the characters can't go over it. I have written a script for it, but for some reason the "lifting up" is not working as intended. It won't go back to its starting place, but it will go a bit below. And for some reason it won't go smoothly to the place where it should, just "teleport" there and done. I thougt multiplying Time.deltaTime with a const will help, but it is the same.
Here is my code, any help would be appreciated:
public class LiftingPlatform : MonoBehaviour {
private Transform lift;
private bool isCanBeLifted;
private float timeToLift;
public float timeNeededToLift = 5f;
private Vector3 startPos;
private Vector3 downPos;
private Vector3 shouldPos;
private bool isDown;
public GameObject[] collidingWalls;
// Use this for initialization
void Start () {
lift = transform;
isCanBeLifted = true;
timeToLift = 0f;
isDown = false;
startPos = transform.position;
downPos = new Vector3(startPos.x, startPos.y - 5f, startPos.z);
}
// Update is called once per frame
void Update () {
timeToLift += Time.deltaTime;
if (timeToLift >= timeNeededToLift) {
if (isCanBeLifted) {
if (isDown) {
shouldPos = Vector3.Lerp(startPos, downPos, Time.deltaTime * 10);
lift.position = new Vector3(shouldPos.x, shouldPos.y, shouldPos.z);
isDown = true;
}
else if (!isDown) {
shouldPos = Vector3.Lerp(downPos, new Vector3(startPos.x, startPos.y, startPos.z), Time.deltaTime * 10);
lift.position = new Vector3(shouldPos.x, shouldPos.y, shouldPos.z);
isDown = false;
}
}
timeToLift = 0;
}
if (!isDown) {
for (int i = 0; i < collidingWalls.Length; i++) {
collidingWalls[i].SetActive(true);
}
}
else if (isDown) {
for (int i = 0; i < collidingWalls.Length; i++) {
collidingWalls[i].SetActive(false);
}
}
}
void OnTriggerEnter(Collider collider) {
if (collider.tag == "Player" || collider.tag == "Enemy") {
isCanBeLifted = false;
}
}
void OnTriggerExit(Collider collider) {
if (collider.tag == "Player" || collider.tag == "Enemy") {
isCanBeLifted = true;
}
}
}
These lifting platforms are a child of another Platforms object.
It doesn't look like you are updating the object's position every frame. You are only checking if the total time passed is greater than the time needed to lift, and then updating the position to a value that is dependent on the delta time (using the Vector3.Lerp function).
What I would do is in the update step, if timeToLift is greater then timeNeededToLift, subtract the latter from the former and invert the value of isDown. Then, in your Vector3.Lerp, make the third argument (timeToLift / timeNeededToLift) instead of (Time.deltaTime * 10). Can you try that and see if it works?
The third argument for Vector3.Lerp is the "blending factor" between the two vectors, 0 is the first vector, 1 is the second, and 0.5 is in between. If the total time is greater than the time needed to lift, but the delta time is not greater than 1, it will get the position of the platform using a blending factor of less than 1, resulting in a platform that didn't move fully.