Okay so here is the code:
// Variable to store character animator component
Animator animator;
// Variables to store optimized setter/getter parameter IDs
int isWalkingHash;
int isRunningHash;
// Variable to store the instance of the PlayerInput
PlayerInput input;
// Variables to store player input values
Vector2 currentMovement;
bool movementPressed;
bool runPressed;
// Awake is called when the script instance is being loaded
void Awake()
{
input = new PlayerInput();
// Set the player input values using listeners
input.CharacterControls.Movement.performed += ctx => {
currentMovement = ctx.ReadValue<Vector2>();
movementPressed = currentMovement.x != 0 || currentMovement.y != 0;
};
input.CharacterControls.Run.performed += ctx => runPressed = ctx.ReadValueAsButton();
}
// Start is called before the first frame update
void Start()
{
// Set the animator reference
animator = GetComponent<Animator>();
// Set ID references
isWalkingHash = Animator.StringToHash("isWalking");
isRunningHash = Animator.StringToHash("isRunning");
}
// Update is called once per frame
void Update()
{
handleMovement();
}
void handleMovement()
{
// Get parameter values from animator
bool isRunning = animator.GetBool(isRunningHash);
bool isWalking = animator.GetBool(isWalkingHash);
// Start walking if movement pressed is true and not already walking
if (movementPressed && !isWalking) {
animator.SetBool(isWalkingHash, true);
}
// Stop walking if movementPressed is false and not already walking
if (!movementPressed && isWalking) {
animator.SetBool(isWalkingHash, false);
}
// Start running if movement pressed and run pressed is true and not already running
if ((movementPressed && runPressed) && isRunning) {
animator.SetBool(isRunningHash, true);
}
// Stop running if movement pressed or run pressed is false and not currently running
if ((!movementPressed || !runPressed) && isRunning) {
animator.SetBool(isRunningHash, false);
}
}
void OnEnable()
{
//Enable the character control action map
input.CharacterControls.Enable();
}
void OnDisable()
{
//Disable the character control action map
input.CharacterControls.Disable();
}
I am using UnityEngine.InputSystem at the start. But movement works fine, just when I let go WASD it is still moving in the last direction pressed. I really have no idea what is the problem here. I am just starting out so I don't know answers to the most simple questions. Sorry for bothering you. Thanks in advance!
in the following block
// Stop walking if movementPressed is false and not already walking
if (!movementPressed && isWalking) {
animator.SetBool(isWalkingHash, false);
}
you said you are checking 'not already walking'.
won't it be '!isWalking' as the if condition?
I'm very new to unity and I'm making a simple tag game with car like players. However, I'm having problems with my trigger collider. When I tag the other car it kind of flickers the is tagged true/false. Here is my code.
public Color TaggedColor;
public Color NoTaggedColor;
public bool Tagged;
// Start is called before the first frame update
void Start()
{
Tagged = true;
GetComponent<Image>().color = TaggedColor;
}
// Update is called once per frame
void Update()
{
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "P2")
{
if (Tagged == false)
{
Tagged = true;
GetComponent<Image>().color = TaggedColor;
}
else
{
if (Tagged == true)
{
Tagged = false;
GetComponent<Image>().color = NoTaggedColor;
}
}
Debug.Log("TAG!");
}
}
void GotTagged()
{
if (Tagged == false)
{
Tagged = true;
//GetComponent<Image>().color = TaggedColor;
}
}
void TaggedOtherPlayer()
{
if (Tagged == true)
{
Tagged = false;
GetComponent<Image>().color = NoTaggedColor;
}
}
I have a Second code for P2 that the only change is the start and the tag is P1. I'm not sure if this problem is something in my code or if it's the collider it's self. If anyone has any idea why this is happening I would love to hear it!
Update: I bevile the issue is coming from the Colliders because I have an empty as a child of both players that is the trigger collider. However when I tag one the other trigger goes off as well. Should I have only one trigger? And if so how would I go about doing it?
Thanks
once you tag a player SetActive(false) the empty triggers for about 1 second or so. If you had a button that whenever its pressed it changes s value from true to false the false to true if you press and hold that button it will "flicker" back and forth between true and false. Thats what's happening with your players the trigger is constantly being pulled while you're touching so make it inactive for long enough for you to not be touching anymore. I hope that makes sense.
I am trying to populate various objects in Unity based on some triggers and some data. The data that I am using looks something like this Key === O2 Values are ==== { collected, collected, collected, absent}Key === O3 Values are ==== { collected, collected, present } stored in a Dictionary object Dictionary<string, List<string>> textMap. Following script is attached to these objects:
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class SensePlayerProximity : MonoBehaviour {
bool disableEntry = false;
bool disableExit = false;
public bool isCollected = false;
//List<Collider2D> triggerList = new List<Collider2D>();
// Use this for initialization
static Dictionary<string, List<string>> textMap = new Dictionary<string, List<string>>
{
{ "O2", new List<string>() { "present", "absent", "absent", "absent" }},
{ "O3",new List<string>() { "absent", "absent", "absent" }}
};
private void OnTriggerEnter2D(Collider2D collision)
{
if (disableEntry || isCollected)
return;
StartCoroutine(disableTriggersForThisCollectible(10));
List<String> values;
foreach (KeyValuePair<string, List<string>> kvp in textMap)
{
values = kvp.Value;
int foundAtIndex = values.IndexOf("absent");
if (foundAtIndex > -1)
{
gameObject.GetComponent<TextMeshProUGUI>().text = kvp.Key;
values[foundAtIndex] = "present";
textMap.Remove(kvp.Key);
textMap.Add(kvp.Key, values);
logTextMap();
return;
}
}
// if nothing is found, then default the text to empty, since nothing left to be collected now
gameObject.GetComponent<TextMeshProUGUI>().text = "";
}
//called when something exits the trigger
private void OnTriggerExit2D(Collider2D collision)
{
if (disableExit || isCollected)
return;
//Debug.Log("Player is leaving me.. :(");
string key = gameObject.GetComponent<TextMeshProUGUI>().text;
StartCoroutine(disableTriggersForThisCollectible(0.1f));
List<string> values = textMap[key];
int index = -1;
if (values != null)
index = values.IndexOf("present");
if(index >= 0)
{
values[index] = "absent";
textMap.Remove(key);
textMap.Add(key, values);
gameObject.GetComponent<TextMeshProUGUI>().text = "";
logTextMap();
}
}
IEnumerator disableEntryTrigger(float t)
{
disableEntry = true;
// disable the trigger collider for t seconds
yield return new WaitForSeconds(t);
disableEntry = false;
}
IEnumerator disableExitTrigger(float t)
{
disableExit = true;
// disable the trigger collider for t seconds
yield return new WaitForSeconds(t);
disableExit = false;
}
void logTextMap()
{
string debugString = "";
foreach (KeyValuePair<string, List<string>> kvp in textMap)
{
debugString += "Key === " + kvp.Key + " Values are ==== { " + String.Join(", ", kvp.Value.ToArray()) + " }";
}
Debug.Log(debugString);
}
}
The script detects trigger collisions with BoxCollider2D attached to my player, and it has "Sensor" tag name attached to it. I disable the triggers for 10s whenever OnTriggerEnter2D event occurs and for 0.1s whenever OnTriggerExit2D event occurs.
I have some fixed textobjects scattered in my level and trying to populate the text in them on the basis of this script above. This script is attached to every such text object.
With the help of these events, I detect if the player is in the vicinity of a text object. If the player is found, then a random key will be populated from the textMap provided that key has at least one value which says "absent". Each key has a list of values, which could be "absent", "present", or "collected". "absent" means that the key is absent from the camera view and thus can be assigned to new collectible text objects. "present" means that the key is present in the current camera view and is not available for the other text objects. "collected" means that the key has already been collected and is not available either. In the example value of textMap which I shown above, for example, there can be 4 copies of the key "O2" in the map, and 3 copies of the key "O3". Out of these, 3 "O2" and 2 "O3" have already been collected. Only 1 copy of "O2" can be assigned to newly triggered text objects and no copy of "O3" is available for them. The script works mostly as expected, except a few time which I am not able to debug. The debug log show that one copy of "O3" is already present in the view, but I went to my scene and could not find "O3" anywhere. I am afraid that this might be happening because all the triggered text objects (to which the above script is attached) are trying to modify the textMap at the same time. I have wasted a lot of time trying to figure this out, but I am just banging my head in the wall. I'd really appreciate if someone could point me in the right direction. My scene with these objects is shown below:
Edit: I found the problem to be DontDestroyOnLoad. The said gameobjest to which SensePlayerProximity script is attached are all children of a DontDestroyOnLoad gameobject called ScenePersist. The problem is happening only when I reload the scene, upon player death. When the scene reloads, new ScenePersist is loaded in the scene, and just before it called the triggerentry method on children before being destroyed. Because of this, OnTriggerEnter2D is called twice, instead of once. How do I fix this problem?
One way to fix this would be to keep all these objects far away from the player spawn point, so that the triggers doesn't occur, but that is not a good way to fix it. Another is to run a coroutine enableTriggers in start method, i.e. disable the triggers by default, but that is not a good solution either.
void Start () {
disableEntry=true;
disableExit = true;
StartCoroutine(enableTriggers());
}
IEnumerator enableTriggers()
{
yield return new WaitForSeconds(0);
disableEntry = false;
disableExit = false;
}
This is how my scene hierarchy looks like:
Here the ScenePersist is set to DontDestroyOnLoad, and it has a lots of sub-child objects (highlighted as collectible) to which the SensePlayerProximity is attached.
The problem is because you disable the trigger, the Enter and Exit methods are not called in pairs. But no code to protect it.
Enter -> present +1, absent -1
Exit -> present -1, absent +1
If the method Enter is called twice, but the method Exit is skipped, now 2 elements in the dictionary become present, but there is only one object (with text O2/O3) in the scene.
Enter -> present +1, absent -1
Left without trigger Exit
Wait 10 seconds
Enter -> present +1, absent -1
Exit -> present -1, absent +1
I solved it by using the OnDestroy method. Before getting destroyed, the object was triggering the OnTriggerEnter2D method. So on getting destroyed I did something similar to what OnTriggerExit2D was doing, as shown below.
void OnDestroy()
{
string key = gameObject.GetComponent<TextMeshProUGUI>().text;
Debug.Log("destroed with key "+ key);
if (!textMap.ContainsKey(key))
return;
List<string> values = textMap[key];
int index = -1;
if (values != null)
index = values.IndexOf("present");
if (index >= 0)
{
values[index] = "absent";
textMap.Remove(key);
textMap.Add(key, values);
gameObject.GetComponent<TextMeshProUGUI>().text = "";
}
}
Currently I'm simply trying to change the sprites candle from unlit to lit when the player has 'picked up' both the candle and the matches and the candle will 'go out' after a certain amount of time. However, when the space bar is pressed the transition from unlit to lit isn't occurring, even though the debug log is returning true when it should. I'm posting here to get some guidance as I have spent most of the day looking online and literally have no idea how to proceed.
Basically the images I am trying to transition between are two different images which are in the sprites folder under assets.
This is what I've got so far.
//the two sprites transition
public Sprite unlitCandle;
public Sprite litCandle;
private SpriteRenderer spriteRenderer;
bool pickUpMatches = false;
bool pickUpCandle = false;
float timeRemaining =5;
bool candleLit = false;
// Use this for initialization
void Start () {
spriteRenderer = GetComponent<SpriteRenderer>();
if (spriteRenderer.sprite == null)
spriteRenderer.sprite = unlitCandle;
}
// Update is called once per frame
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.CompareTag("Matches"))
{
collision.gameObject.SetActive(false);
pickUpMatches = true;
}
if (collision.gameObject.CompareTag("UnlitCandle"))
{
collision.gameObject.SetActive(true);
pickUpCandle = true;
}
}
public void CandleTimer()
{
if (candleLit == true)
{
timeRemaining = 5;
timeRemaining -= Time.deltaTime;
if (timeRemaining <= 0)
{
candleLit = false;
spriteRenderer.sprite = unlitCandle;
}
}
}
public void ChangeSprite()
{
if (spriteRenderer.sprite == unlitCandle)
{
spriteRenderer.sprite = litCandle;
}
}
void Update () {
if (pickUpCandle == true && pickUpMatches == true)
{
//Debug.Log(candleLit);
if (Input.GetKey(KeyCode.Space) && !candleLit)
{
CandleTimer();
ChangeSprite();
Debug.Log(timeRemaining);
candleLit = true;
//Debug.Log(candleLit);
}
}
}
}
Try comparing with a method like equals() instead of == in
spriteRenderer.sprite == unlitCandle
Because right now you are just comparing references and not the objects.
At least I think thats the problem.
There are a few possible issues with your code. First, you are calling changeSprite at the top of Update, which means that it is unconditionally being called every frame. Therefore, after a single frame of your candle being unlit, it will immediately change its sprite to litCandle.
I assume that the reason you are calling changeSprite every frame is in order to process the timer if you have a lit candle already. Really, you should move the code to process the timer (your whole second if statement in changeSprite) to a separate function and name it something like processCandleTimer. Call that at the top of Update and save the changeSprite method to only be called on the keypress.
Lastly, the issue that I suspect is giving you the most trouble is that you aren't resetting your timer, timeRemaining. The first time you light the candle the timer will go down to 0 after the 5 seconds pass. Every time changeSprite is run after that, you will change the sprite to litCandle in the first if statement and then immediately change it back to unlitCandle because the timer is 0 in the second. To remedy this, you need to add a line like timeRemaining = 5.0f; when the key is hit.
Essentially I have a door trigger event where if the player presses the switch then the door open. I wish however to do it so that if the door associated needs two switches to open well... then it only opens if two switches are pressed. Here is my code for my InteractSwitch
public class InteractSwitch : MonoBehaviour
{
Animator animator;
public DoorEventTrigger[] doorTriggers;
public bool pressed;
private bool down;
// Use this for initialization
void Start ()
{
animator = GetComponent <Animator> ();
}
// Update is called once per frame
void Update ()
{
}
void OnCollisionEnter2D (Collision2D target)
{
if (target.gameObject.tag == "Player") {
down = true;
animator.SetInteger ("SwitchTrig", 1);
foreach (DoorEventTrigger trigger in doorTriggers)
if (trigger != null)
trigger.Toggle (true);
}
}
When triggered this event checks for player, shows switch has been pressed then sends a bool to a function called Toggle which handles the operation of the door.
Next I have my DoorEventTrigger event which checks if the bool sent = true. If it is then the door will open. Here is where I am stuck. As seen in the code I have created a array of InteractSwitch which stores the amount of switches I want the player to have pressed before the door will open. I then state that if the length > 1 then this if condition is true and some code will be added here which will then open the door only if the player has selected the InteractSwitch[].length switches. My question is how would I check that all instances of InteractSwitch for this DoorEventTrigger equals true?
Here is my DoorEventTrigger code
public void Toggle (bool val)
{
if (switchTriggers.Length > 1) {
Debug.Log ("HAS THIS ACTUALLY DONE ANYTHING");
door.Open ();
}
else
{
if (val)
door.Open ();
else
door.Close ();
}
}
}
The solution I came up with is nowhere near I believe the most efficient answer as it requires code each time u add a new switch but this is what I have
firstly in the InteractSwitch script the bool down is public so it can be accessed by the switchtriggers
public bool down;
Next is the added code to the Toggle() method
public void Toggle (bool val)
{
if (switchTriggers.Length > 1) {
if((switchTriggers[0].down == true) && (switchTriggers[1].down == true)){
Debug.Log ("THIS ACTUALLY DONE SOMETHING!");
door.Open ();
}
}
else
{
if (val)
door.Open ();
else
door.Close ();
}
}
Now from the public SwitchInteract[]; I know the exact amount of elements in the array however this could be checked through switchTriggers.length; Knowing there is two elements in the array my condition switchTriggers.Length > 1 is correct so next I take the two elements in the array and check if they are down; if BOTH are down then the condition continues and the Debug.Log() validates this. Finally Open() is called which opens the door.
You can have the switches increment or decrement a required switch variable in the door object.
Give the door object a requiredSwitch variable.
Give each switch a door variable and set it to the door it controls.
When a switch is activated, have it decrement its door's requiredSwitch variable.
When the door's requiredSwitch variable is 0, then the door opens.
You can also have the switches increment the variable if they get deactivated.
This solution allows you to add switches by just setting the door variable of the switch and incrementing the door's initial requiredSwitch variable.