I have created a script that receives a List of game object. When I press the tab key, the script must update the index by 1 (index++), get the gameobject in that position, then log the name of the gameobject, this is the piece of code for the script
void Start() {
index = 0;
}
void Update(){
ChangeLeader();
}
void ChangeLeader(){
if (index >= playerList.Count){
index = 0;
}else{
if (Input.GetKey(KeyCode.Tab)){
leader = playerList[index];
index += 1;
Debug.Log(leader.name);
}
}
}
What I was expecting was that every time I press tab, it will log the objects name then updates the index in 1 unit.
The problem is that I was expecting the code to log all the names in the List at once.
It don't feel discouraged to call me dumb. I only started with unity a few days ago, thanks for the help.
Update() is called every frame.
(Input.GetKey(KeyCode.Tab)) is gonna be true for consecutive frames, because your finger is down for a few milliseconds.
Use:
(Input.GetKeyDown(KeyCode.Tab))
or
(Input.GetKeyUp(KeyCode.Tab)) instead. This is triggered just once.
Related
I have a joystick display picture for my game. Currently, when the player touches the screen the image disappears and when the player is not touching the screen, it reappears. I wrote that using an if else statement.
if (indicator.inputIndicator.x != 0)
{
joystick.SetActive(false);
}
else
{
joystick.SetActive(true);
}
The problem is, I want the image to reappear after some time like 2 seconds. I want to delay the "else", but I do not want to use a coroutine. I want "else" to work after 2 seconds since the player takes his hand off the screen but I couldn't figure out how to do it. any help will be great.
Setting a timer is a pretty common problem you have to solve in Unity. One basic approach is to have a variable that you add Time.deltaTime every update. That way you can tell how long it has been since some condition was met.
Every Update iteration that meets the condition, add Time.deltaTime to the variable. If at some point the condition fails, reset the variable to 0. Then you can just base your joystick.SetActive() call on the value of your variable.
For example, your script might become:
float thresholdTimeToShowPrompt = 2;
// By starting at the threshold, the image is hidden at the start until a touch
float timeSincePlayerTouch = 2;
void Update()
{
// Rather than calling SetActive directly, just update the timer
if (indicator.inputIndicator.x != 0)
{
timeSincePlayerTouch = 0;
}
else
{
timeSincePlayerTouch += Time.deltaTime;
}
// Now we can base visibility on the time since the last user touch
bool shouldShowIcon = timeSincePlayerTouch >= thresholdTimeToShowPrompt;
// Only call SetActive when needed, in case of overhead
if (shouldShowIcon && !joystick.activeSelf)
{
joystick.SetActive(true);
}
else if (!shouldShowIcon && joystick.activeSelf)
{
joystick.SetActive(false);
}
}
I am trying to make a script that saves a moving object's position when a certain amount of time passes and then when i press a button i want that object to return to that saved position, but i don't have any idea on how to save the position of the object. can anyone help me with this?
As I understand the main challenge is that you do not want only one single position stored but rather a continuous storage of the positions up to 3 seconds ago.
So as I see it this means you would need to keep track of all frames positions within the last 3 seconds.
Not sure about performance - there might be way better solutions - but the most straight forward I came up with for now would be using a Queue<Vector3> (= first-in fist-out) and a simple initial delay of 3 seconds
The idea here is:
You have a Queue<Vector3> (= first-in / first-out) for the past frames positions
Each frame in Update you add the current transform.position to the end of the queue
You have an initial delay of 3 seconds - during this period you simply pile up positions into the Queue
Once this initial delay has passed, once a frame you pull out the first stored position (from 3 seconds ago) of the Queue and assign it to a field currentResetPostion
=> When you press your key the position from 3 seconds ago is always available in currentResetPostion. Then you reset and restart the position tracking
This could look somewhat like e.g.
[Header("Settings")]
[SerializeField] private float timeToTrackBack = 3f;
[SerializeField] private KeyCode keyToPressForReset = KeyCode.Space;
// keeps track of all frames positions
private readonly Queue<Vector3> previousPositions = new();
// The current valid position to reset to when pressing the button
private Vector3 currentResetPostion;
// Timer field for the initial delay
private float delayTimer;
// doing this in OnEnable so you could even decide to disable and enable this component rather then tracking always
private void OnEnable()
{
RestartTracking();
}
private void Update()
{
// was space pressed this frame?
if (Input.GetKeyDown(KeyCode.Space))
{
ResetPosition();
}
else
{
// otherwise pile up positions to be handled later on
TrackPosition();
// and handle the next position from the queue
HandleQueueDelayed();
}
}
private void HandleQueueDelayed()
{
// reduce the timer by the time passed since last frame
delayTimer -= Time.deltaTime;
// Only once the timer reached 0 we start to pull out the first stored position
// (from 3 seconds ago) once a frame
if (delayTimer <= 0 && previousPositions.Count > 0)
{
currentResetPostion = previousPositions.Dequeue();
}
}
private void TrackPosition()
{
previousPositions.Enqueue(transform.position);
}
private void ResetPosition()
{
// reset position
transform.position = currentResetPostion;
// and restart the tracking
RestartTracking();
}
private void RestartTracking()
{
// forget any remaining positions since we want to start a new fresh tracking
previousPositions.Clear();
// store the current positions as the reset fallback for the next 3 seconds
currentResetPostion = transform.position;
// restart the timer
delayTimer = timeToTrackBack;
}
You might of course want to extend this e.g. including the rotation or in case you are using a Rigidbody you would rather want to shift this into FixedUpdate and only go through Rigidbody.position and additionally reset the Rigidbody.velocity etc
So I'm trying to add a particle effect to a little space game of mine, I get axis "Vertical", then check if it is greater than 0 the particle system plays (going forward)
flyfloat = Input.GetAxis("Vertical");
if(flyfloat > 0)
{
particles.Play();
}
else
{
particles.Stop();
}
That controls whether it is playing the particle system, but the issue i have is that it only gives some particles and then stops, I've viewed the flyfloat and it is at 1.
What may the problem be here?
Thanks
You question is incomplete as for example I don't know where you are using these lines of code.. inside an Update() method or a Start() method.
Assuming you are calling it in Update() method. Let me explain first what is happening wrong here. So as Update() gets called each frame when you pressing UP_Arrow key flyfloat = 1 . that's ok but now as you go inside the if loop to check flyfloat > 0 and calls partciles.Play() it is being called every Update() loop means every frame so what's happening is your ParticleSystem is getting played every frame so not playing at all. Also whenever you stops pressing the UP_Arrow key the flyfloat = 0 for which it's going inside the else loop and stops playing the ParticleSystem.
So to solve this you can introduce a Boolean which makes partciles.Play() and partciles.Stop() gets called once when you are pressing UP_Arrow key.
below code will make the ParticleSystem play when you press UP_Arrow key and stops it when you press DOWN_Arrow key
public ParticleSystem particles;
public float flyfloat;
bool isParticlePlaying = false;
private void Update()
{
flyfloat = Input.GetAxis("Vertical");
if (flyfloat > 0 && !isParticlePlaying)
{
particles.Play();
isParticlePlaying = true;
}
else if (flyfloat < 0 && isParticlePlaying)
{
particles.Stop();
isParticlePlaying = false;
}
}
I am creating a 2D platform game with Unity and C#. I searching for how I can do the following.
I have 5 sets of animations and one gameObject(later in game there should be more than one object that can be changed, so maybe we can keep that in mind) with the tag "Campfire0" stored in anim0 inside the start function. What I want is when the user pressed E on his keyboard it will check which is the current animation of the current gameObject and change that animation to "Camfire25" or another number like 50, 75 or 100 and update the gameObject tag. After that is done, it should again check all if statements and the second time you pressed E it should look at the if statement with if(anim25) and so on.
Problem
Below code I use currently. But the problem is the following. I declare inside the start function the animator of each game object tag, but this will be fired one time if the game is started. After pressing the second time on E, the error "NULL" NullReferenceException will occur. So I think I should store the values inside an array? And how should I do that.
For checking which animation is the current one, I use if statements, but can it also be done with a foreach loop or is there a shorter way to do this?
Current Code:
public Animator anim0;
public Animator anim25;
public Animator anim50;
public Animator anim75;
public Animator anim100;
// Use this for initialization
void Start () {
count = 0;
setCountText ();
currentValue = startingValue;
//anim = GameObject.Find("Campfires").GetComponent<Animator>(); // Parent of all Campfires
anim0 = GameObject.FindGameObjectWithTag("Campfire0").GetComponent<Animator>();
anim25 = GameObject.FindGameObjectWithTag("Campfire25").GetComponent<Animator>();
anim50 = GameObject.FindGameObjectWithTag("Campfire50").GetComponent<Animator>();
anim75 = GameObject.FindGameObjectWithTag("Campfire75").GetComponent<Animator>();
anim100 = GameObject.FindGameObjectWithTag("Campfire100").GetComponent<Animator>();
}
void Update () {
if (inTrigger){
if (Input.GetKeyDown(KeyCode.E) && count > 0){
// Update counter on press
updateCount();
if (anim0){
anim0.SetTrigger ("Campfire25");
GameObject.FindGameObjectWithTag("Campfire0").transform.tag = "Campfire25";
}
if (anim25){
Debug.Log ("hello");
anim25.SetTrigger ("Campfire50");
GameObject.FindGameObjectWithTag("Campfire25").transform.tag = "Campfire50";
}
if (anim50){
anim50.SetTrigger ("Campfire75");
transform.tag = "Campfire75";
}
if (anim75){
anim75.SetTrigger ("Campfire100");
transform.tag = "Campfire100";
}
}
}
}
Above script works for the first time, it will change the campfire tag to "Campfire25" and show another animation clip, after pressing E for the second time it will give me an error of NullReferenceException. This is because the Start Function will be fired ones.
I have a "datapack" hidden in each of my levels, the system remembers per level if the artifact has been taken and if so it disables the artifact when a player replays it but i also want to keep track of the total amount taken, the system almost works there's just 1 small issue:
When i finish the level with the first datapack, in the menu it says: 1, this is correct.
but when i do it with the 2nd, it says 3, with the 3rd it says 4, 4th says 5 etc
i don't understand why and thus can't fix it
here's the code:
function OnTriggerEnter(other : Collider) {
if(other.CompareTag("DataPacket")){
pickedUpDataPacket = 1;
Destroy(other.gameObject);
gameObject.Find("DatapackFound").guiText.enabled = true;
yield WaitForSeconds (1.5);
gameObject.Find("DatapackFound").guiText.enabled = false;
}
if(other.CompareTag("Finish") && pickedUpDataPacket == 1){
PlayerPrefs.SetInt("DataPackLevel" + levelindex.ToString(), 1);
//if(!PlayerPrefs.HasKey("totalDatapacks")){
//PlayerPrefs.SetInt("totalDatapacks", 1);
//} else {
PlayerPrefs.SetInt("totalDatapacks", (PlayerPrefs.GetInt("totalDatapacks")+1));
}
}
//}
i already commented a part out, i believe this was also part of the issue.
and part of a 2nd script:
if(datapacktotal){
if(PlayerPrefs.GetInt("totalDatapacks") > 0){
findText.text = "Collected:" + PlayerPrefs.GetInt ("totalDatapacks");
}
Thanks in advance :)
From the described behaviour it seems that, in some occasions, the collision is triggered multiple times, before the datapack is being destroied. As in level 2, where the counter passes from 1 to 3.
My though is that your collider has multiple contact points, so that - if you touches n of them - the collision is triggered n times.
I would try a simple experiment, just use a flag to determine if that's the first time you "touch" the collider; then, you'll update the PlayerPref just in that case:
function OnTriggerEnter (other : Colliderstrong text){
if (collisionAlreadyConsidered) return;
collisionAlreadyConsidered = true;
// your code here...
}
function Update(){
collisionAlreadyConsidered = false;
}
, where collisionAlreadyConsidered is a global variable.