Semi-continued movement after Time.timescale = 0 - c#

I'm trying to make text boxes pop up during my game and have it pause the game, and it seems to be working for the most part. There's a little bit of oddness with it that I've found and can't figure out what's going on: As long as I keep hitting the movement keys (particularly LEFT) I can keep moving.
My game is tile and turn based, so the movements are simply testing the map array and then jumping the character (as follows):
if (Input.GetButtonDown ("Horizontal"))
{
if (Input.GetAxis ("Horizontal") > 0)
{
if (TileArray.WalkableTile (newPosition.x + 1, newPosition.y) == true)
{
TileArray.ToggleWalkable (newPosition.x, newPosition.y, true);
newPosition.x += 1;
PlayerMoved = true;
}
}
else if (Input.GetAxis ("Horizontal") < 0)
{
if (TileArray.WalkableTile (newPosition.x - 1, newPosition.y) == true)
{
TileArray.ToggleWalkable (newPosition.x, newPosition.y, true);
newPosition.x -= 1;
PlayerMoved = true;
}
}
}
The code I'm using to pause and display the text boxes is:
public void StartDialog(int Counter)
{
Time.timeScale = Time.timeScale = 0;
InDialog = true;
DialogCounter = Counter;
DialogCanvas.enabled = true;
ClickCounter = 0;
DialogText.text = DialogArray [DialogCounter][ClickCounter];
UpdateDialogPic(DialogArray[DialogCounter][ClickCounter]);
}
It does not seem to matter whether the text is only 1 box or a chain of multiple text boxes that you cycle through using mouse clicks. Everything seems to be working fine aside from this, and it doesn't matter whether I wait to press left or not - as long as I don't stop once I START, I can walk the map or until I hit something.

You need to note two main facts about Time.timeScale.
When timeScale is set to zero the game is basically paused if all your
functions are frame rate independent.
and
If you lower timeScale it is recommended to also lower
Time.fixedDeltaTime by the same amount.
You may make the following changes to your code,
// introduce a new variable
bool isPaused
// for the pausing/resuming action
if(isPaused)
{
Time.timeScale = 1;
isPaused = !isPaused;
}
else
{
Time.timeScale = 0;
isPaused = !isPaused;
}
// On your 'if (Input.GetButtonDown ("Horizontal"))' line
if (Input.GetButtonDown ("Horizontal") && !isPaused)

Related

Unity2D using coroutines for tracking when the player is turning?

I am using skeletons to animate the player. I have 3 fundamental skeletons to transition between when the player turns:
Player in profile,
player turning and
player facing-the-camera
to face left or right, the renderer is simply rendered mirror-image using:
if (movement > 0) transform.localScale = new Vector2(1f, 1f);
if (movement < 0) transform.localScale = new Vector2(-1f, 1f);
however, capturing the intermediate state where a player is moving from right to left (or vice versa) is proving tricky.
I setup a 3 renderers to operate as below:
void Start()
{
rightRenderer.enabled = true;
turningRenderer.enabled = false;
centerRenderer.enabled = false;
}
and I had been operating on the assumption that a coroutine could be used to measure the movement of Input.GetAxis("Horizontal"); from left-to-right (or vice versa). So I put together various experiments based around the below:
private List<float> trackedMovement = new List<float>();
private bool isTurning;
private float speed = 0f;
private int turningCounter = 0;
public IEnumerator countdownTurn()
{
while (0 < turningCounter--)
{
if (turningCounter == 0)
{
StopCoroutine("countdownTurn");
break;
}
yield return new WaitForSeconds(0.2f);
}
}
public IEnumerator turn()
{ // speed can be between 0 and 2.5f;
if (speed > 0 && speed <= 0.5f)
{
while (speed != 0)
{
trackedMovement.Add(movement);
trackedMovement.ForEach(f =>
{
if (
((f > 0 && movement < 0) ||
(f < 0 && movement > 0)))
{
isTurning = true;
trackedMovement = new List<float>();
turningCounter = 5;
StartCoroutine("countdownTurn");
}
});
yield return new WaitForSeconds(0.02f);
}
}
}
void Update()
{
StartCoroutine("turn");
speed = defaultAnimator.GetFloat("Speed");
turn();
}
the theory in my head was that turningCounter could demonstrate states of player by number
say I move from left to right:
turningCounter = 4 player is turning from left-towards-camera.
rightRenderer.enabled = false;
turningRenderer.enabled = true;
centerRenderer.enabled = false;
turningCounter = 3 || 2 player is facing camera.
rightRenderer.enabled = false;
turningRenderer.enabled = false;
centerRenderer.enabled = true;
turningCounter = 1 player is turning from camera-toward-right-profile.
rightRenderer.enabled = false;
turningRenderer.enabled = true;
centerRenderer.enabled = false;
turningCounter = 0 player is fully facing right.
rightRenderer.enabled = true;
turningRenderer.enabled = false;
centerRenderer.enabled = false;
all my experiments have yielded results that are inconsistent.
If I place the renderer routines inside the sub-coroutine countdownTurn, they don't always fire when the player turns left/right.
if I try and measure the turning counter in any update statement (I have tried standard, fixed and default without much success) I get stale feedback on the turningCounter value (ie. it sometimes gives me some of the countdown but not all of the countdown).
so my question is:
Are coroutines the best way to address this? if so, are there any recommendations on how I might get a clean and consistent turning counter value from them?
If coroutines aren't a good solution, are there any other methods people have taken to create the desired result?
[EDIT] as suggested by #Everts, use the current localscale as the basis for working out the change in direction.
if localscale.x < 0 and movement > 0 then we can surmise that the player wants to turn (for this contrived example). likewise is true for localscale.x > 0 and movement < 0.
so we are able to draft the following Update (I attach here a simplified version ignoring things like crouching mechanics etc.):
void Update()
{
movement = Input.GetAxis("Horizontal");
if (((transform.localScale.x > 0 && movement < 0) ||
(transform.localScale.x < 0 && movement > 0))
&& !isTurning)
{
isTurning = true;
turningCounter = 5;
StartCoroutine("countdownTurn"); // simply ticks down turningCounter every 0.n seconds
}
if (turningCounter == 3) setRenderDirection(); // ie. routine for transforming localscale (mirroring the image)
// show player in profile at end of counter
profileRenderer.enabled = turningCounter < 1;
// show player turning towards camera on 4 and away from camera on 1
turningRenderer.enabled = turningCounter == 4 || turningCounter == 1;
// show player looking dead center for 2 counts
centerRenderer.enabled = turningCounter == 2 || turningCounter == 3;
if (turningCounter < 1)
{
isTurning = false;
defaultAnimator.SetBool("isTurning", false);
}
}

How can I make a smooth transition between two animations?

First animation when starting the game the character is examine or typing and then after 10 seconds I want the character to start walking and just before reaching the destination to change to idle.
The order should be : Typing , Walking , Idle
With this script the character is typing waiting 10 seconds rotating looking the target and then move to the target without the two animations Walking and Idle.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AgentControl : MonoBehaviour
{
public List<Transform> points;
public bool waitTimeToMove = false;
//notice WaitTime is a float now
public float WaitTime = 10f;
public bool randomWaitTime;
float waitMinTime = 1f;
float waitMaxTime = 10f;
public bool loop = false;
private int destPoint = 0;
private NavMeshAgent agent;
private Transform originalPos;
//two vars for handling timer
private float timer = 0;
private float originSpeed;
void Start()
{
agent = GetComponent<NavMeshAgent>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
agent.autoBraking = false;
originSpeed = agent.speed;
if (randomWaitTime == true)
{
WaitTime = Random.Range(waitMinTime, waitMaxTime);
}
//transforms dont exist without A GameObject and a GameObject doesn't exist without a transform
//create a new GameObject to hold our position
GameObject originalPositionObject = new GameObject();
originalPositionObject.name = "WP";
originalPositionObject.tag = "Waypoint";
originalPositionObject.transform.parent = GameObject.Find("Waypoints").transform;
//set the new gameobjects position equal to where the transform is right now
originalPositionObject.transform.position = transform.position;
//add this to the points list instead
points.Add(originalPositionObject.transform);
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Count == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Count;
}
void Update()
{
// Choose the next destination point when the agent gets
// close to the current one.
if (!agent.pathPending && agent.remainingDistance < 1f)
{
//if wait to move is true
if (waitTimeToMove)
{
//if timer is less than 10
if (timer < WaitTime)
{
//add Time.deltaTime each time we hit this point
timer += Time.deltaTime;
}
//no longer waiting because timer is greater than 10
else
{
waitTimeToMove = false;
}
}
//if we hit here waitToMove is false, so go ahead as usual
else
{
if (loop == false && destPoint == points.Count - 1)
{
agent.speed = 0;
}
if (loop == true || destPoint != points.Count - 1)
{
agent.speed = originSpeed;
// Not working if setting back to loop = true in the inspector
// After it was not loop and agent in last waypoint
// When setting to loop = true it's not continue to check why
// Should continue the loop if loop true !
// Loop = true is not working when game is running only on Start
GotoNextPoint();
}
}
}
}
}
In the editor I have an animator controller for the character with 3 states : The touc/examine , walk , idle
The first animation is working fine but it's not changing to walk after 10 seconds and not changing to walk at all. It keep looping the first animation all the time. and the first animation length is more then 0 seconds.
between the first animation state and the second walk state there is a transition and exit time is disabled with one condition Walk True and a parameter I added of type bool name Walk.
between the second state walk and the last state idle also there is a transition and exit time disabled too with condition Walk True.
Screenshot of the first transition :
The main idea is to change transitions smooth between the 3 states and then stop when the last state played without looping. Each state should play once.
What I did so far :
In both transitions disabled Has Exit Time (enabled false)
Then changed the script added a line to start the Walk animation state :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class AgentControl : MonoBehaviour
{
public List<Transform> points;
public bool waitTimeToMove = false;
//notice WaitTime is a float now
public float WaitTime = 10f;
public bool randomWaitTime;
float waitMinTime = 1f;
float waitMaxTime = 10f;
public bool loop = false;
public Animator anim;
private int destPoint = 0;
private NavMeshAgent agent;
private Transform originalPos;
//two vars for handling timer
private float timer = 0;
private float originSpeed;
void Start()
{
agent = GetComponent<NavMeshAgent>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
agent.autoBraking = false;
originSpeed = agent.speed;
if (randomWaitTime == true)
{
WaitTime = Random.Range(waitMinTime, waitMaxTime);
}
//transforms dont exist without A GameObject and a GameObject doesn't exist without a transform
//create a new GameObject to hold our position
GameObject originalPositionObject = new GameObject();
originalPositionObject.name = "WP";
originalPositionObject.tag = "Waypoint";
originalPositionObject.transform.parent = GameObject.Find("Waypoints").transform;
//set the new gameobjects position equal to where the transform is right now
originalPositionObject.transform.position = transform.position;
//add this to the points list instead
points.Add(originalPositionObject.transform);
anim = GetComponent<Animator>();
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Count == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Count;
}
void Update()
{
// Choose the next destination point when the agent gets
// close to the current one.
if (!agent.pathPending && agent.remainingDistance < 1f)
{
//if wait to move is true
if (waitTimeToMove)
{
//if timer is less than 10
if (timer < WaitTime)
{
//add Time.deltaTime each time we hit this point
timer += Time.deltaTime;
}
//no longer waiting because timer is greater than 10
else
{
waitTimeToMove = false;
anim.SetBool("Walk", true);
}
}
//if we hit here waitToMove is false, so go ahead as usual
else
{
if (loop == false && destPoint == points.Count - 1)
{
agent.speed = 0;
}
if (loop == true || destPoint != points.Count - 1)
{
agent.speed = originSpeed;
// Not working if setting back to loop = true in the inspector
// After it was not loop and agent in last waypoint
// When setting to loop = true it's not continue to check why
// Should continue the loop if loop true !
// Loop = true is not working when game is running only on Start
GotoNextPoint();
}
}
}
}
}
After 10 seconds :
anim.SetBool("Walk", true);
but now I want that a bit before or when the agent get to the target destination to change it to the Idle state animation :
anim.SetBool("Idle", true);
but I'm not sure where to put it in the script. I tried in this place :
if (loop == false && destPoint == points.Count - 1)
{
agent.speed = 0;
anim.SetBool("Idle", true);
}
but it didn't work fine the agent kept walking after the target destination then moved back to the target destination then changed to idle a mess.

Not cycling through an array as expected

I'm trying to get the player to continue on after he jumps (see code), but what he does is return back to the jumpPosition transform without continuing on to the next point.
Here is what it is doing in Unity:
...
Here is my code for playerMovment:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class MovementController : MonoBehaviour
{
// public GameObject playerToMove; // not sure why I need this
public float moveSpeed; // move speed of the player going from player postion to current point, possible to use somewhere else
private Transform currentPoint; // used to determine where the next point the player has to move by cycling through 'points' array
public Transform jumpPoint; // used as a location trigger to tell the player when to jump -- will be attempting to make into an array
public Transform crouchPoint; // used as a location trigger to tell the player when to crounch -- will be attempting to make into an array
public Transform[] points; // an array of location for the 'currentPoint' to cycle through
public float maxPause = 100; // used to determine the length of time between when the player arrives at the 'currentPoint' and when to leave said point; default = 100
public float reducedPause = 2; // used to set 'maxPause' to a smaller number so that player won't keep jumping/crouching
public TestCharacterController2D controller; // acceses the TestCharacterController2D script (I didnt write this script but plan to modiify) used for basic move, jump, and crouch funtions
public Animator animator; // my attempt to find the player's animator
public bool isRight; // used to to determine which way the character is facing -- I think this can be accesed through the 'controller' variable (TestCharacterController2D script)
private bool jump; // to tell the 'controller' when to jump
private bool crouch; // to tell the 'controller' when to crouch
private bool pause = false; // used to determine when the player arrives at the 'currentPoint' and the 'maxPause' countdown begins
public int pointsSelection; // used to cycle the 'points' array when maxPause cycle is over and player is at current point
// public float jumpHeight = 100f; // not sure why used
void Start() // looking into 'onAwake' maybe? (or others)
{
currentPoint = points[pointsSelection]; // sets currentPoint to default location ('pointSelection' is 'publc' so can be modified in Unity
isRight = true; // player starts facing right -- as per character animations
}
void Update() // not sure if should have more in 'FixedUpdate' or others (maybe?)
{
jump = false;
if (Vector2.Distance(transform.position, currentPoint.position) < 0.05f)
// checks to see if player is at 'currentPoint'
{
pause = true; // starts the pause sequenece
Debug.Log("Pause = " + pause);
if (pause) // when the movement is pause do the the following
{
moveSpeed = 0;
animator.SetFloat("Speed", 0); // player stops moving -- works!
if (maxPause <= 100) // checks to see if still paused
{
Debug.Log("this is maxPause: " + maxPause);
if (maxPause < 0) // found 'maxPause' was going to far below zero
maxPause = 0;
maxPause--; // reduce pause amount (working way out of loop)
}
if (maxPause == 0) // when 'maxPause' timer has finished
{
pointsSelection++; // move to next point
maxPause = 100; // reset 'maxPause' timer
pause = false; // resume 'transform.position == currentPoint.position' process
}
}
if (pointsSelection == points.Length) // makes sure 'pointsSelection' doesn't go out of bounds
{
Debug.Log("at end of array");
pointsSelection = 0; // start the player's movement process over again
}
}
else // not sure if requried
{
Debug.Log("pause = false");
Debug.Log("this is the moveSpeed " + moveSpeed);
Debug.Log("pointsSelection: " + pointsSelection);
}
if (Vector2.Distance(transform.position, jumpPoint.position) < 0.05f && jumpPoint == currentPoint) // conditions for the jump action (automatic) -- I fell the whole thing needs to be more elaborate ** WORK IN PROGRESS **
{
jump = true;
}
else
jump = false;
currentPoint = points[pointsSelection]; // moved to line 130 -- not sure if better here
}
void FixedUpdate()
{
if (isRight && transform.position.x > currentPoint.position.x) // flipping the character -- I'm pretty sure I can use TestCharacterController2D to do this for me, this is comparing the player's 'transform'
{
moveSpeed = -0.25f; // tells controller to head in the left direction
isRight = false; // no longer facing right
}
if (!isRight && transform.position.x < currentPoint.position.x) // reverse of above
{
moveSpeed = 0.25f; // tells controller to head in the right direction
isRight = true; // no longer facing left
}
if (moveSpeed > 0 || moveSpeed < 0)
animator.SetFloat("Speed", 1); // player starts PlayerRun animation -- works!
// Move our character
controller.Move(moveSpeed, crouch, jump); // draws from the TestCharacterController2D script
}
public void OnLanding()
{
animator.SetBool("Jumped", false);
}
}

Jump height in relation to timeheld of spacebar

This has been asked before, but none of the answers solved my problem because the problem was always slightly different.
I have a character that jumps on spacebar down. I want it to make higher jumps when the spacebar is pressed longer, with a maximum of twice the normal jump height.
Here's what I came up with so far:
void FixedUpdate () {
if (Input.GetKeyDown(KeyCode.Space) && myRB.velocity.y == 0)
{
timeHeld = 1;
}
if (Input.GetKey(KeyCode.Space) && myRB.velocity.y == 0)
{
myRB.AddForce(Vector3.up * 20,ForceMode2D.Impulse);
timeHeld += Time.deltaTime;
myRB.mass = myRB.mass - timeHeld/10;
}
if (Input.GetKeyUp(KeyCode.Space))
{
myRB.mass = 1;
}
}
Since the jump has to happen on keydown (not keyup), I can't increase the force added on jump, because the force is already applied on keydown. Therefore, I was thinking of lowering the mass of my rigidbody.
The above code "works" if I keep holding spacebar: the character jumps (minimum height), lands, jumps again but now a bit higher, etc. But that's not what I'm looking for, obviously.
This can easily be done by making a Time frame in which Jumping occurs;
Within this time frame u allow upward forces to be applied by holding space bar down. As soon as you reach either the end of the time frame or the end of the button press; you restrict upward velocity until the character has landed.
Let gravity take its own course after you end the jump.
As for the coding part i suggest you start a co routine when the jump happens.
here is some code to u help you get started;
IEnumerator Jumper()
{
if (maxjumpduration!=jumpduration)
{
jumpduration = jumpduration + 0.1f;
yield return new WaitForSeconds(0.1f);
}
else
{
jumpduration = 0;
StopCoroutine(Jumper());
}
}
As for the fact you want have a regular jump as well; make another time frame in which is decided if the press of space bar is considered either a regular jump or a controlled jump; During that initial time frame in which you are still deciding what kind of jump it is; you execute a short part of the jump until you reach a conclusion and then you send it to the correct method.
I added Some ground work for u but try to finish the code yourself;
{
private bool shortjump = false;
private bool checkphase = false;
private bool hitground = false;
private Rigidbody player = new Rigidbody();
private bool jumping = false;
Vector3 sharedjumpforce = new Vector3();
private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Initialjump();
}
if (Input.GetKeyUp(KeyCode.Space))
{
if (jumping)
{
shortjump = true;
}
}
if (jumping)
{
if (hitground)
{
jumping = false;
}
}
}
void Initialjump()
{
if (jumping = false)
{
checkphase = true;
jumping = true;
player.AddForce(sharedjumpforce);
Invoke("Standardorcontrolledjump", 0.2f);
}
}
void Standardorcontrolledjump()
{
checkphase = false;
if (shortjump)
{
}
else
{
}
}
}
I don't understand why are you putting
&& myRB.velocity.y == 0
You want it to reduce its mass while in air don't you. Assuming that reducing the mass after the initial force has been applied does allow the Rigidbody to go higher, (force of gravity weakens, im not sure if that happens in Unity)
Why don't you try this
void FixedUpdate () {
// apply force the instant button is pressed. That is only once
if (Input.GetKeyDown(KeyCode.Space) && myRB.velocity.y == 0)
{
myRB.AddForce(Vector3.up * 20,ForceMode2D.Impulse);
timeHeld = 1;
}
// keep subtracting from mass while its held
if (Input.GetKey(KeyCode.Space))
{
timeHeld += Time.deltaTime;
myRB.mass = myRB.mass - timeHeld/10;
}
if (Input.GetKeyUp(KeyCode.Space))
{
myRB.mass = 1;
}
}

Box collider doesn't catch mouse button press event

I have a cupboard with 2 colliders - one for cupboard and one for it's box. When I press on the box, I want to open/close it. It worked fine, but now by some reason it only work when I press on the edge on the box. When click on the center, it don't work.
Video: https://youtu.be/OozsAi7KNzs
Here is the code, which play animation (open/close cupboard), when I press on the box:
public Animation[] animations;
public string[] animationName;
public bool playOneDirection; // should revert animation speed after second playing?
public AudioSource myAudioOpen;
public AudioSource myAudioClose;
private bool isDoorClosed;
private bool isAimationReadyToPlay = true;
private Collider thisCollider;
public void Start()
{
thisCollider = GetComponent<Collider>();
}
void Update ()
{
if (Input.GetButton("Fire1"))
if(DoPlayerLookAtButton() && isAimationReadyToPlay)
OpenCloseDoor();
}
bool DoPlayerLookAtButton()
{
RaycastHit _hit;
Ray _ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
bool isHit = Physics.Raycast(_ray, out _hit, 1.5f);
if (isHit && _hit.collider == thisCollider) return true;
else return false;
}
public void OpenCloseDoor()
{
if (!isDoorClosed) // Play animation with normal speed
{
myAudioOpen.Play();
for (int i = 0; i < animations.Length; i++)
{
animations[i][animationName[i]].speed = 1.0f;
animations[i].Play();
}
}
if(playOneDirection)
return;
if (isDoorClosed) // Play animation with revert speed
{
myAudioClose.Play();
for (int i = 0; i < animations.Length; i++)
{
animations[i][animationName[i]].speed = -1.0f;
animations[i][animationName[i]].time = animations[i][animationName[i]].length;
animations[i].Play();
}
}
StartCoroutine("DelayBetweenAnimations");
isDoorClosed = !isDoorClosed;
}
IEnumerator DelayBetweenAnimations()
{
isAimationReadyToPlay = false;
yield return new WaitForSeconds(0.5f);
isAimationReadyToPlay = true;
}
Your cupboard has 2 colliders, but you are only checking for one of them. If there is some kind of overlap then it could be fiddly to click the correct one. If you just want to be able to click anywhere on the game object change your code like so...
//From
//if (isHit && _hit.collider == thisCollider) return true;
//To
if (isHit && _hit.transform.gameObject == this.gameObject) return true;
Add a layer mask for your player and ensure your Physics.Raycast excludes that layer, to avoid the cast from hitting yourself. See here
I've made the main camera starting from the center of the player, so raycast hits player's collider. I made it trying to fix the bug when camera can go throuth the wall like on the screen below.
Raycast can be seen passing through the player and did not reach the box

Categories