Unity Ai GameObject Keeping increasing his speed when he moving - c#

i did some code making an enemy search if the player exists or not and if the player exists go-to player to kill him the code is working fine but every moment the enemy move the speed keep increased and i do not want the speed increased at all so how to make it moving at the same starting speed without any increase
this is my code
void Update () {
if (target == null) {
if (!searchingForThePlayer) {
searchingForThePlayer = true;
StartCoroutine (searchForPlayer ());
}
return;
}
if (Vector3.Distance (target.position, transform.position) < 20) {
transform.position = Vector2.MoveTowards (transform.position, target.position, 1f * Time.deltaTime);
if (target.position.x < transform.position.x && !facingRight)
Flip ();
if (target.position.x > transform.position.x && facingRight)
Flip ();
} else {
}
StartCoroutine (UpdatePath ());
}
IEnumerator searchForPlayer () {
GameObject sRusult = GameObject.FindGameObjectWithTag ("Player");
if (sRusult == null) {
yield return new WaitForSeconds (0.5f);
StartCoroutine (searchForPlayer ());
} else {
target = sRusult.transform;
searchingForThePlayer = false;
StartCoroutine (UpdatePath ());
yield return null;
}
}
IEnumerator UpdatePath () {
if (target == null) {
if (!searchingForThePlayer) {
searchingForThePlayer = true;
StartCoroutine (searchForPlayer ());
}
yield return null;
} else {
if (Vector3.Distance (target.position, transform.position) < 20) {
transform.position = Vector2.MoveTowards (transform.position, target.position, 1f * Time.deltaTime);
if (target.position.x < transform.position.x && !facingRight)
Flip ();
if (target.position.x > transform.position.x && facingRight)
Flip ();
} else {
}
}
// Start a new path to the target position, return the result to the OnPathComplete method
yield return new WaitForSeconds (1f / 2f);
StartCoroutine (UpdatePath ());
}
void Flip () {
Vector3 scale = transform.localScale;
scale.x *= -1;
transform.localScale = scale;
facingRight = !facingRight;
}

Every frame, in your Update() method, you start the UpdatePath() coroutine. Then, in UpdatePath(), you start another UpdatePath() coroutine. In no situation do you don't start it, so this ensures that UpdatePath() keeps running forever and ever.
Since you also keep starting a new one in Update(), this means that you keep piling on more and more coroutines, which means the more the game runs, the more UpdatePath() gets called every frame.
in other words, your object's speed isn't technically increasing, it's just the number of time MoveTowards() is called that does, which does have the same end result.
As for the fix, I'd recommend restructuring your code. For example, I find it highly suspicious that Update() and UpdatePath() are near identical copies of each other. I also find it weird that UpdatePath() starts itself at the end of its run.

Related

Enemies stop spawning after x amount have spawned/been destroyed?

I am very new to c# and I've come across a problem with my enemy spawner. I am making an endless top-down shooter, and after 20 enemies have been spawned and then destroyed, the enemies stop appearing. My score counter continues to increase though, which leads me to believe they're somehow being destroyed.
Here is the enemy spawn controller:
void Update()
{
if (!spawningObject && GameController.EnemyCount < spawnSettings[0].maxObjects)
{
spawningObject = true;
float pick = Random.value * totalWeight;
int chosenIndex = 0;
float cumulativeWeight = enemySpawnables[0].weight;
while(pick > cumulativeWeight && chosenIndex < enemySpawnables.Count - 1)
{
chosenIndex++;
cumulativeWeight += enemySpawnables[chosenIndex].weight;
}
StartCoroutine(SpawnObject(enemySpawnables[chosenIndex].type, Random.Range(spawnSettings[0].minWait / GameController.DifficultyMultiplier, spawnSettings[0].maxWait / GameController.DifficultyMultiplier)));
Spawn();
}
}
private IEnumerator SpawnObject(string type, float time)
{
yield return new WaitForSeconds(time);
randomSpawnPoint = Random.Range(0, enemySpawners.Length);
randomEnemy = Random.Range(0, enemy.Length);
enemyPool.SpawnObject(enemySpawners[randomSpawnPoint].position, transform.rotation);
spawningObject = false;
GameController.EnemyCount++;
}
And here is the enemy controller (attached to enemy prefab):
public void Start()
{
myRB = GetComponent<Rigidbody>();
player = FindObjectOfType<PlayerController>();
}
private void OnEnable()
{
player = FindObjectOfType<PlayerController>();
}
void Update()
{
if (health <= 0)
{
Die();
}
}
void FixedUpdate()
{
transform.LookAt(player.transform.position);
myRB.velocity = (transform.forward * moveSpeed);
//Falling
if(myRB.velocity.y < 0)
{
myRB.velocity += Vector3.up * Physics.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
}
}
public void Die()
{
print("Enemy" + this.gameObject.name + " has died!");
EnemySpawn.instance.enemyPool.ReturnObject(this.gameObject);
player.GetComponent<PlayerController>().points += pointsToGive;
ScoreController.scoreValue += 1;
}
The only thing that should affect enemy health is the bullet. Here is the part of the bullet controller that affects enemy health:
public void OnTriggerEnter(Collider other)
{
if(other.tag == "Enemy")
{
triggeringEnemy = other.gameObject;
triggeringEnemy.GetComponent<EnemyController>().health -= damage;
PlayerController.instance.bulletPool.ReturnObject(gameObject);
}
}
The issue always happens after 20 enemies have been spawned/destroyed and 20 points have been achieved. Before then, enemy movement is normal and everything seems to be working as it should. It could very well be something very simple that I'm missing, but I'd appreciate any help!
When you do engage logic mistake good way to find it is Visual Studio debugging.
Here is good official article about whole process - https://docs.unity3d.com/Manual/ManagedCodeDebugging.html.
I would place breakpoint right at start of Update method to see that EnemyCount increases over time and then you will understand that you forgot to do decrement it.

How to make a vector2 slowly go to a number [0]?

I have a ship which is moving around with rigidbody velocity, and when the player has no input I want the ship to slow down to 0 and then stay at 0 until there's more input. What I have now is:
if(yAxis == 0)
{
StartCoroutine (StopRoutine());
}
IEnumerator StopRoutine()
{
yield return new WaitForSeconds (0.5f);
Vector2 force = rb.velocity;
rb.velocity = (new Vector2(0.2f,0.2f)) - force;
}
and all that does is make the ship go backward which I understand is due to it subtracting forever with no clamp, I just don't know how to make it go to 0 and STAY at 0, I also don't know how I would do it the other way too (making a vector2 which is in the negatives go to 0)
Actually you able to do many different ways. If want a use coroutines for this sample. Try this at update method in your script...
First of all you need to declare a variable to check have any input? (I think this is wind, fuel etc.)
bool isInput = true;
IEnumerator stopRoutine(float targetX, float targetY, float valueX, float valueY, float t) //targetX and targetY your target speed values, valueX and valueY is your acceleration or deceleration values and t is waitTime for while loop...
{
if (isInput)
{
isInput = !isInput; // You've to do this because unity update function re-runs every deltaTime.
Vector2 firstVelocity = rb.velocity;
if (firstVelocity.x < 0f && firstVelocity.y < 0f)
{
while (rb.velocity.x < targetX || rb.velocity.y < targetY)
{
rb.velocity += new Vector2(valueX, valueY);
yield return new WaitForSecondsRealtime(t);
}
}
else
{
while (rb.velocity.x > targetX || rb.velocity.y > targetY)
{
rb.velocity -= new Vector2(valueX, valueY);
yield return new WaitForSecondsRealtime(t);
}
}
}
}
I don't want to do it for you completely, but I want you to understand the logic in the event. Ok try this method in update()...

How to delay movement in unity while animation plays

I am having difficulty delaying the movement of my character in Unity 2018.4, I suspect this is more of just a problem with my code as apposed to unity.
Edit:
I want to be able to hold down the right arrow key, have the character not move for 415ms, then be able to move for 580ms, after which he cannot move for 350ms (to let the animation finish playing)
I tried using countdown in a IE numerator to wait and then call my movement function but this results in my character not moving after the animation has played.
//What makes the character move, works fine if executed on its own in update
private void ExecuteMovement(float dir)
{
currentPosition = transform.position;
currentPosition.x += dir * Time.deltaTime * speed;
transform.position = currentPosition;
}
//Trying to use waitforseconds to delay the execution while the animation plays
private IEnumerator Movement(float dir)
{
yield return new WaitForSeconds(0.5f);
ExecuteMovement(dir);
}
void Update()
{
if (0 > Input.GetAxis("Horizontal"))
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
Movement(Input.GetAxis("Horizontal"));
}
else if (0 < Input.GetAxis("Horizontal"))
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
Movement(Input.GetAxis("Horizontal"));
}
}
I'm happy to try any alternatives to delaying movement of the character. The animation shows the character charging up to jump and then jumps. I want to only move while it is in the air which is approximately half a second into the animation.
You are trying to call your IEnumerator like a method.
Instead you have to start it as a Coroutine using StartCoroutine
StartCoroutine(Movement(Input.GetAxis("Horizontal")));
Saw your edit
I want to be able to hold down the right arrow key, have the character not move for 415ms, then be able to move for 580ms, after which he cannot move for 350ms (to let the animation finish playing)
until now so since you want a continues movement but still fully control the speed I would keep it in Update and use the coroutine only for controlling a flag. Coroutine is still way better to read/write and maintain. (Now it also allows direction switch midair .. not sure if you want this or not):
// control the ability to move with a simple flag
// instead of various states
public bool canMove;
// check if a jumping is already being animated/executed
// -> disallow a new jumping animation until it is finished
private bool canJump = true;
private void Update()
{
// avoid repeated API calls
var input = Input.GetAxis("Horizontal");
if (input < 0)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
if(canJump)
{
//starts the movement
StartCoroutine(Jump());
}
}
else if (input > 0)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
if(canJump)
{
//starts the movement
StartCoroutine(Jump());
}
}
// if can not move do nothing else
if(!canMove) return;
// execute the movement
transform.position += Vector3.right * input * speed * Time.deltaTime;
}
private IEnumerator Jump()
{
// disable jumping
canJump = false;
// disable move during animation
canMove = false;
//starts animation
animator.SetBool("isSkipping", true);
// not move for 415ms
yield return new WaitForSeconds(0.415f);
// enable move
canMove = true;
// be able to move for 580ms
yield return new WaitForSeconds(0.580f);
// disable move
canMove = false;
// cannot move for 350ms during end of animation
yield return new WaitForSeconds(0.350f);
// enable move again
canMove = true;
// re-enable jumping
canJump = true;
}
How about using timer in Update?
float timer = 0f;
void Update()
{
var dir = Input.GetAxis("Horizontal");
if (0 > dir)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 180, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
timer +=Time.deltaTime;
if(timer >= 0.5){
ExecuteMovement(dir);
timer -=0.5f;
}
//Movement(Input.GetAxis("Horizontal"));
}
else if (0 < dir)
{
//This is to flip image
transform.eulerAngles = new Vector3(0, 0, 0);
//starts animation
animator.SetBool("isSkipping", true);
//calls the movement function with the direction to move in
timer +=Time.deltaTime;
if(timer >= 0.5){
ExecuteMovement(dir);
timer -=0.5f;
}
//Movement(Input.GetAxis("Horizontal"));
}
}
I found a rather crude way of making this work after heavily modifying Gray_Rhino's answer to this question. I set up 3 stages of my jump animation, when continually moving one direction a timer will check to see how far into the animation it is and decide whether or not to allow movement input. the timer resets after the animation is finished or the character is told to move in the other direction.
bool facingRight;
int jumpPhase = 1;
float timer = 0f;
float dir;
void Update()
{
if (jumpPhase != 3)
{
dir = Input.GetAxis("Horizontal");
}
if (0 > dir || (jumpPhase == 3 && facingRight == true))
{
timer += Time.deltaTime;
if (facingRight != true)
{
transform.eulerAngles = new Vector3(0, 180, 0);
jumpPhase = 1;
timer = 0f;
facingRight = true;
}
else if (jumpPhase == 1)
{
//starts animation
animator.SetBool("isSkipping", true);
if (timer >= 0.415f)
{
jumpPhase = 2;
}
}
else if (jumpPhase == 2)
{
ExecuteMovement(dir);
if (timer >= 0.995f)
{
jumpPhase = 3;
}
}
if (jumpPhase == 3)
{
if (timer >= 1.5f)
{
jumpPhase = 1;
timer = 0f;
}
}
}
else if (0 < dir || (jumpPhase == 3 && facingRight != true))
{
timer += Time.deltaTime;
if (facingRight == true)
{
transform.eulerAngles = new Vector3(0, 0, 0);
jumpPhase = 1;
timer = 0f;
facingRight = false;
}
else if (jumpPhase == 1)
{
//starts animation
animator.SetBool("isSkipping", true);
if (timer >= 0.415f)
{
jumpPhase = 2;
}
}
else if (jumpPhase == 2)
{
ExecuteMovement(dir);
if (timer >= 0.995f)
{
jumpPhase = 3;
}
}
if (jumpPhase == 3)
{
if (timer >= 1.5f)
{
jumpPhase = 1;
timer = 0f;
}
}
}
}

Everything in the scene except the player looks blurry when the camera is moving | Unity2D

I started off my game using a simple camera follow script which only followed my player on the x axis with an offset and and some restrictions
I recorded a video on my android to show what I mean. In the recording the issue is a bit exaggerated ( due to recording) and only viewable once the spikes enter the view. When not recording, the player animation is perfectly smooth. Here's the video
using UnityEngine;
public class MainCamera : MonoBehaviour {
public Transform target;
public float xOffset, xPosRestrictionMin, xPosRestrictionMax;
private float yPos, zPos;
void Start ()
{
yPos = transform.position.y;
zPos = transform.position.z;
}
void LateUpdate ()
{
transform.position = new Vector3(Mathf.Clamp(target.position.x + xOffset, xPosRestrictionMin, xPosRestrictionMax), yPos, zPos);
}
}
However when first running the game everything looked "jittery". Despite all my player physics being inside fixed update, input inside update and camera updates being inside lateupdate. I tried setting interpolate to extrapolate on the players rigidbody2d. Now the player would look and animate smoothly but everything else look blurry when the camera is moving. I thought that maybe my script or settings were at fault so I tried to turn off vsync, set target frame rate to 60 and when nothing worked I downloaded Cinemachine. Even with cinemachine instead of my own script it still looks blurry and I can't figure out why. Here's a simplified version of my controls.
private void Update()
{
//Handle touch input
if (Input.touchCount > 0)
{
foreach (Touch touch in Input.touches)
{
switch (touch.phase)
{
// Touchdown
case TouchPhase.Began:
if (onGround || !doubleJumped)
{
jump = true;
touchReleased = false;
if (onGround)
anim.SetBool("jump", true);
}
break;
//Touch up
case TouchPhase.Ended:
allowGlide = false;
anim.SetBool("glide", false);
if (rb.velocity.y > 0)
touchReleased = true;
break;
}
}
}
}
void FixedUpdate()
{
rb.velocity = new Vector2(newMoveSpeed, rb.velocity.y);
onGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
if (onGround && !jump)
{
gliding = false;
allowGlide = false;
anim.SetBool("glide", false);
anim.SetBool("jump", false);
doubleJumped = false;
}
//Slowly Accelerate if not at top speed and touching the ground
if (newMoveSpeed < moveSpeed && onGround)
newMoveSpeed += 0.0165f;
anim.speed = Mathf.Clamp(newMoveSpeed, 13, 18);
//Jumping
if (jump && onGround)
{
jump = false;
rb.velocity = new Vector2(rb.velocity.x, jumpHeight);
}
else if (jump && !doubleJumped && !onGround)
{
jump = false;
doubleJumped = true;
allowGlide = true;
rb.velocity = new Vector2(rb.velocity.x, jumpHeight);
}
//Add multiplier if falling down
if (rb.velocity.y < 0 && allowGlide)
{
anim.SetBool("glide", true);
if (!gliding)
{
rb.velocity -= Vector2.up * Physics2D.gravity.y;
gliding = true;
}
else
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (glideMultiplier - 1) * Time.deltaTime;
}
}
else if (rb.velocity.y < 0)
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
}
//Increase fall multiplier if touch is released mid jump
else if (rb.velocity.y > 0 && touchReleased)
{
rb.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpMultiplier - 1) * Time.deltaTime;
}
else if (rb.velocity.y == 0)
{
return;
}
}
Thanks, any help or feedback is appreciated!
Have you checked the frame rate of your game running on device? I have a film/animation background (but new to Unity) and it looks to me like you have a frame rate issue. Although, if the recording software changed the graphics at all I'm not sure I am seeing the same thing you are. Why does your recording software do that? Have you tried using Rec? It works great for me.
Sorry for not posting this as a comment - my current rep won't allow me to.
Maybe i think this is causing issues with the recording in the device which maybe took some computation power and caused issues, does this same thing happens while just playing and not recording?

Waitforseconds is not waiting

I have this code which is loaded when you finish a game level. It should freeze the time, scale the level completed message, wait 5 seconds and then load next level and unfreeze time. The problem is that the WaitForSeconds is not working. It just loads the next level without waiting. What am I doing wrong?
IEnumerator WaitingFunction(){
yield return new WaitForSeconds(5);
}
void OnTriggerEnter ( Collider col )
{
if (col.tag == "Player")
{
Time.timeScale = 0f;
lvlcomplete.transform.localScale = new Vector3 (1,1,1);
StartCoroutine(WaitingFunction());
Time.timeScale = 1f;
lvlcomplete.transform.localScale = new Vector3 (0,0,0);
Application.LoadLevel(Application.loadedLevel+1);
player.transform.position = new Vector3(0f, 0.3519309f, -2.381933f);
}
You start a coroutine that waits 5 seconds without doing anything while the OnTriggerEnter code continues to load the level instantly.
You should move the code that should run after the wait time has passed inside the coroutine:
IEnumerator WaitingFunction(){
yield return new WaitForSeconds(5);
Time.timeScale = 1f;
lvlcomplete.transform.localScale = new Vector3 (0,0,0);
Application.LoadLevel(Application.loadedLevel+1);
player.transform.position = new Vector3(0f, 0.3519309f, -2.381933f);
}
Like LearnCocos2D said you have to move the code. Further on to get this working with timeScale = 0 you simply need to let this wait for the corresponding number of frames:
IEnumerator WaitingFunction(){
int noOfFramesToWait = (int)(seconds * Application.targetFrameRate);
for (int i = 0; i < noOfFramesToWait; i++) {
yield return null;
}
Time.timeScale = 1f;
// do my stuff
}
If you have a couple of situations like pausing, additive level loading, game over, fading in and out,.. the next step can be get this more generic:
void OnTriggerEnter ( Collider col ) {
if (col.tag == "Player") {
Time.timeScale = 0f;
StartCoroutine (WaitForNFrames (5f, LoadNewLevel));
}
}
public void LoadNewLevel () {
Time.timeScale = 1f;
// do my stuff
}
IEnumerator WaitForNFrames (float seconds, System.Action executeWhenFinished) {
int noOfFramesToWait = (int)(seconds * Application.targetFrameRate);
for (int i = 0; i < noOfFramesToWait; i++) {
yield return null;
}
executeWhenFinished ();
}
Another small thing, maybe you have this already: You reference the player object after calling LoadLevel. Ensure that it is not destroyed when the new level is loaded by using DontDestroyOnLoad.
Try restructuring your code as follows
WaitingFunction(){
yield WaitForSeconds (5);
}
void OnTriggerEnter ( Collider col )
{
if (col.tag == "Player")
{
Time.timeScale = 0f;
lvlcomplete.transform.localScale = new Vector3 (1,1,1);
StartCoroutine(WaitingFunction());
Time.timeScale = 1f;
lvlcomplete.transform.localScale = new Vector3 (0,0,0);
Application.LoadLevel(Application.loadedLevel+1);
player.transform.position = new Vector3(0f, 0.3519309f, -2.381933f);
}

Categories

Resources