I am trying iterate through an array of Bullets in order to set the position of each one with a delay of 0.3 secs.
FireTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (kbState.IsKeyDown(Keys.Space) && FireTimer > FireRate)
{
for (int i = 0; i < bullets.Count(); i++)
{
if (bullets[i].IsAlive == false)
{
bullets[i].IsAlive = true;
bullets[i].position = position;
FireTimer = 0f;
}
}
}
My FireTimer is assigned a value of 0.0f and the FireRate is assigned 0.3f.
All bullets are drawn and given the same position when I call this method. Can anybody tell me what needs to be done to get them to be positioned at the position in 0.3sec intervals?
You are checking the firetimer when the key is down, then you iterate over the entire array and fire each bullet. You will want a variable to store the current index in the clip, then increment it each time a bullet is fired.
int clipIndex = 0;
...
FireTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (kbState.IsKeyDown(Keys.Space) && clipIndex < bullets.Count() FireTimer > FireRate)
{
if (bullets[clipIndex].IsAlive == false)
{
bullets[i].IsAlive = true;
bullets[i].position = position;
FireTimer = 0f;
}
clipIndex++;
}
You could then reload by changing the index back to 0; Another thing to be aware of, if the bullet's flag IsAlive is true, then this miss fire for a single frame. If you wanted to just find the next alive bullet you could do a while statement:
while (bullets[clipIndex].IsAlive && clipIndex < bullets.Count())
clipIndex++;
if (clipIndex < bullets.Count())
...
The if statement changes because you know that clipIndex will be the index of a bullet that isn't alive, but you do want to make sure there IS a bullet.
Related
I'm trying to detect long touch on the screen by using Time.time or Time.deltatime, but nothing works.
It should turn out like this: after beginning TouchPhase.Stationary (or Began) timer should goes on, then after TouchPhase.Ended timer can be reset to 0.
But here is condition that checks, if I held the button on the sensor less than 0.5 sec - do something.
Problems:
Time.Deltatime for some reason it does not add up to a variable for each iteration, but constantly assigns a new value equal to the time of the last frame, somewhere around 0.02-0.1
Time.time constantly adds a value to the variable, when subtracting the time of pressing the screen and the vacation time, the variable that should be reset to zero, but with each iteration of actions, the value for some reason cannot vary from 0.0 - 0.05, the timer for some reason constantly sums up the value and the variable constantly increases
My ask: how can I track how many seconds my finger was on the screen?
void TapOrLongTounch()
{
float pressTime = 0;
float endPressTime = 0;
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Stationary)
{
pressTime += Time.time;
}
if (touch.phase == TouchPhase.Ended)
{
endPressTime = Time.time - pressTime;
if (endPressTime < 0.5f)
{
//Do something;
}
endPressTime = 0;
}
}
}
First of all Time.time is
This is the time in seconds since the start of the application [...].
What you are doing is basically exponentially adding more and more time every frame.
→ What you rather want to count is the Time.deltaTime
The interval in seconds from the last frame to the current one [...].
like
if (touch.phase == TouchPhase.Stationary)
{
pressTime += Time.deltaTime;
}
so that pressTime results in the total amount of time in seconds the touch was stationary.
BUT you also have it as a method variable so it continuously starts from 0 anyway!
→ You rather want to have a class field for it!
Then further the
if (touch.phase == TouchPhase.Ended)
{
endPressTime = Time.time - pressTime;
...
makes no sense / is pretty redundant. The pressTime already is the total time in seconds the touch was stationary!
In general I would rather use a switch-case and do e.g.
float pressTime = 0;
void TapOrLongTounch()
{
if (Input.touchCount <= 0) return;
var touch = Input.GetTouch(0);
switch(touch.phase)
{
// Maybe you also want to reset when the touch was moved?
//case TouchPhase.Moved:
case TouchPhase.Began:
pressTime = 0;
break;
case TouchPhase.Stationary:
pressTime += Time.deltaTime;
break;
case TouchPhase.Ended:
case TouchPhase.Canceled:
if (pressTime < 0.5f)
{
//Do something;
}
pressTime = 0;
break;
}
}
I am trying to make enemy patrolling system, where evrytime guard reaches his point, he stopes for 10 seconds, and then continue his movement. I've tried combining animations from Blend tree with isStopped property from NavMeshAgent.
EDIT: My current script makes agent move to point, then he stopes for some time, and then only walk animation plays, but he staing on one place.
public Transform[] points;
private int destPoint = 0;
public NavMeshAgent agent;
public Animator animator;
public int time;
void Start()
{
agent = GetComponent<NavMeshAgent>();
animator = transform.Find("Enemy").GetComponent<Animator>();
// 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;
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Length == 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.Length;
//agent.speed = 1f;
//animator.SetFloat("Blend", agent.speed);
}
void Update()
{
if (agent.remainingDistance == 0f && time == 100000)
{
agent.speed = 1f;
Debug.Log(agent.remainingDistance);
animator.SetFloat("Blend", 1);
GotoNextPoint();
}
else if (agent.remainingDistance <= 0.5f && agent.remainingDistance != 0f && time == 100000)
{
animator.SetFloat("Blend",0);
agent.enabled = false;
GotoNextPoint();
}
else if(animator.GetFloat("Blend") == 0)
{
time--;
}
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000;
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
agent.autoRepath = true;
GotoNextPoint();
}
}
I changed few lines of code, now agent moves after first stop, but second time he stops at second poitm,walking animation still working, time doesn't decrementing
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000;
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
//New lines of code
agent.ResetPath();
destPoint = (destPoint + 1) % points.Length;
agent.SetDestination(points[destPoint].position);
}[enter image description here][1]
First of all, I would use the "SetDestination" function in order to set the next destination.
In the end you wrote:
if (time == 99000 && animator.GetFloat("Blend") == 0)
{
time = 10000; *-> needs to be 100000, not 10000*
agent.enabled = true;
agent.isStopped = false;
animator.SetFloat("Blend", 1);
agent.autoRepath = true;
GotoNextPoint();
}
You can use "NavMeshAgent.ResetPath" to reset the path instead of using "autoRepath".
After the "ResetPath", use the "SetDestination" inside the function GotoNextPoint().
One more very important thing - Check that there is more than one point.
If there is just one point, your guard will just walk at the same spot.
For more information, is suggest to check out Unity NavMeshAgent
I'm trying to code a behaviour for my boss where it charges at the player but before charging it is supposed to flicker a bit to show intent of charge. Below is how I am achieving this in Update():
if (chargeTimer <= 0)
{
if (!returnToStart)
{
StartCoroutine(TankSpriteFlicker());
if (chargeNow)
{
transform.position = Vector3.MoveTowards(transform.position, chargeTarget, Time.deltaTime * chargeSpeed);
// Target reached? If so, start moving back to the original position
if (Vector3.Distance(transform.position, chargeTarget) <= Mathf.Epsilon)
{
returnToStart = true;
this.chargeTimer = this.chargeRate;
}
chargeNow = false;
}
}
else
{
transform.position = Vector3.MoveTowards(transform.position, tankStartPosition, Time.deltaTime * returnSpeed);
// Original position reached? If so, start moving to the target
if (Vector3.Distance(transform.position, tankStartPosition) <= Mathf.Epsilon)
{
returnToStart = false;
this.chargeTimer = this.chargeRate;
}
}
}
else
{
this.chargeTimer -= Time.time;
}
IEnumerator TankSpriteFlicker()
{
for (int i = 0; i <= 2; i++)
{
tankSprite.color = Color.red;
yield return new WaitForSeconds(0.1f);
tankSprite.color = startColor;
yield return new WaitForSeconds(0.1f);
}
chargeNow = true;
}
chargeTarget is a fixed value of chargeTarget = new Vector3(-2.5f, transform.position.y, transform.position.z);
The problem with this is that it charges, but stops midway and then charges again.
Also as time passes by the flickering becomes very random and it starts flickering during and after a charge..
Here is the problem in action:
https://gfycat.com/latefrigidbullmastiff-rivalsofaether
I can't explain any of this behaviour and cant fix it, any help would be appreciated.
This is not how coroutines work
Lets look at just these lines:
StartCoroutine(TankSpriteFlicker());
if (chargeNow) { ... }
You start a coroutine, that's fine, but your next instruction asks if the coroutine has been completed (as chargeNow is not set to true until the last line of the coroutine). This cannot and will not ever be true at this point in the execution because coroutines are fundamentally "go do this stuff later" and later is not now.
You need to move these additional pieces of logic (the stuff that happens "after" the coroutine) to the end of the coroutine.
I have a shader in Unity that is set up to blend between two textures. This is set up and works fine.
Blend has a range from 0-1 and works well on offline mode.
_Blend1("Blend between _MainTex and Texture2", Range(0, 1)) = 0
I have set up an OnClick pointer that works fine. It essentially toggles a value to true and activates it in the Update. I have had some success on toggling the values between 0 (The first texture and 1 (The second texture).
public void OnColorChangeClick()
{
if (newSwitchOne)
{
switchOn = true;
}
else if (newSwitchTwo)
{
switchOff = true;
}
}
In the update when switchOn is true I have a while loop that runs and increments a count for the blend.
void Update()
{
rend.material.SetFloat("_Blend1", up);
while (switchOn == true) {
for (int i = 0; i < 10; i++)
{
//StartCoroutine(Wait());
up = Time.deltaTime * (up + 0.1f);
}
switchOn = false;
print(switchOn);
}
}
The trouble I am having is that the value increments are not working alongside time.deltaTime. I am seeing that Time.deltatime may not work in a while loop. So I have also tried a CoRoutine with a WaitForSeconds. None of this is giving me the incrementation that I desire.
I have looked into Lerping this - but I'm not aware how to Lerp such a value - I have been looking, perhaps in the wrong places.
Can someone point me in the right direction?
Time.deltaTime returns the time it took to complete the last frame so it will be roughly constant as you loop through (and a very small value) - which is keeping your up value to remain tiny. You should be able to lerp the value like so.
float t = 0;
void Update()
{
if (switchOn == true)
{
t += Time.deltaTime;
rend.material.SetFloat("_Blend1", Mathf.Lerp(0, 1f, t/3f)); //3 is time to complete lerp
}
if (t > 3) // lerp has completed. reset function and timer.
{
t = 0;
switchOn = false;
}
}
Update() already runs "over time." You don't need a for loop or a coroutine. You just need to track a variable that changes inside the Update() method body.
up = Time.deltaTime * (up + 0.1f);
Oh wait, you already do.
void Update()
{
rend.material.SetFloat("_Blend1", up);
if(switchOn == true) {
up = Time.deltaTime * (up + 0.1f);
if(up >= 1) {
switchOn = false;
}
print(switchOn);
}
}
Ta da.
However, you still need code to bring the value back down again (either animating it back to 0 or just setting it to 0) but your original code didn't have it either, so I'm not including it.
I am making an object spawn script in which on the start of the script the spawning function is called and in it is a for loop which creates an object each iteration. It first picks a random X position for it and then checks if it is in a range of another prefabs coordinates so they don't spawn too close or worse, one in each other. If it is in the same coordinates as another prefab it will return 0 and this same goes out for the Z axis too. It also picks a random Y axis rotation so it doesn't all face the same direction. After this it spawns the prefab and sets it's coordinates and rotation after which it check if the coordinates in the X or Z axis are 0, and if any of those two are 0 it goes back one iteration and the last object to be spawned is destroyed so it doesn't flood. This works perfectly but when you want to set it to spawn too much objects it floods the RAM because there is nowhere to spawn more objects. I tried finding the highest X position and highest Z position and multiplying them, and setting them both to positive, and then dividing them by the space between the prefabs but this doesn't work as it sets it to a really really high number. How would you fix this?
Script:
using UnityEngine;
using System.Collections;
public class PrefabSpawner : MonoBehaviour {
public int amountOfPrefabs;
public int maxAmountOfPrefabs;
private int currentSpawnedPrefab;
public float spaceBetweenPrefabs;
private float positionX;
private float positionZ;
private float maxPositionX;
private float maxPositionZ;
private float multipliedPosXZ;
private bool previousSpawnHadZero;
public GameObject prefab;
private GameObject point1;
private GameObject point2;
private GameObject currentSpawn;
private Vector2[] positions;
void Start () {
currentSpawnedPrefab = 0;
previousSpawnHadZero = false;
point1 = gameObject.transform.GetChild (0).gameObject;
point2 = gameObject.transform.GetChild (1).gameObject;
if (point1.transform.position.x > point2.transform.position.x)
maxPositionX = point1.transform.position.x;
else
maxPositionX = point2.transform.position.x;
if (point1.transform.position.z > point2.transform.position.z)
maxPositionZ = point1.transform.position.z;
else
maxPositionZ = point2.transform.position.z;
multipliedPosXZ = maxPositionX * maxPositionZ;
if (multipliedPosXZ < 0)
multipliedPosXZ += multipliedPosXZ + multipliedPosXZ;
maxAmountOfPrefabs = Mathf.FloorToInt (multipliedPosXZ / spaceBetweenPrefabs);
if (amountOfPrefabs > maxAmountOfPrefabs)
amountOfPrefabs = maxAmountOfPrefabs;
point1.GetComponent<MeshRenderer> ().enabled = false;
point2.GetComponent<MeshRenderer> ().enabled = false;
gameObject.GetComponent<MeshRenderer> ().enabled = false;
positions = new Vector2[amountOfPrefabs];
SpawnPrefabs (amountOfPrefabs);
}
void SpawnPrefabs (int amount) {
for (int i = 0; i < amount; i++) {
if(previousSpawnHadZero)
i -= 1;
currentSpawn = (GameObject)Instantiate (prefab);
positionX = GetRandomPositionX ();
positionZ = GetRandomPositionZ ();
currentSpawn.transform.position = new Vector3 (positionX, this.transform.position.y + currentSpawn.transform.localScale.y, positionZ);
currentSpawnedPrefab += 1;
if (positionX == 0 || positionZ == 0) {
previousSpawnHadZero = true;
currentSpawnedPrefab -= 1;
Destroy (currentSpawn);
}
if (positionX != 0 && positionZ != 0) {
previousSpawnHadZero = false;
positionX = 0;
positionZ = 0;
}
}
}
IEnumerator Pause () {
yield return null;
}
float GetRandomPositionX () {
//Finds a random position for the X axis and then checks it and returns either 0 if the position is taken or the position if not
}
float GetRandomPositionZ () {
//Finds a random position for the Z axis and then checks it and returns either 0 if the position is taken or the position if not
}
bool CheckPositionAvailable (float pos, int axis) {
//Checks if the position is available.
}
}
Code is really long to debug but the problem is clearly visible and is from the SpawnPrefabs function. Currently, when you instantiate a prefab, you check if the generated position is 0. If 0, you subtract 1 from the i in the for loop then destroy the instantiated object and then start the for loop again from the current loop-1.
So the combination of Instantiate, Destroy and repeating it over again in the for loop is causing the memory issue.
What to do:
You have to re-write the whole function and this will require modification in your whole code too. Do not instantiate and destroy object in that loop unless when needed.
1.In the Start() function, create one prefab.
2.Make it to be invisible in the scene by disabling its mesh/sprite renderer.
3.Use that prefab in the for loop to check if the generated position is valid. If it is valid, you can now create/instantiate an object in the loop.
This prevents instantiating and destroying objects in the loop when you only create objects when if (positionX != 0 && positionZ != 0) .