How can i set animation command only once in the Update function? - c#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Animations : MonoBehaviour
{
public enum AnimatorStates
{
WALK, RUN, IDLE
}
private Animator _anim;
void Awake()
{
_anim = GetComponent<Animator>();
}
public void PlayState(AnimatorStates state)
{
string animName = string.Empty;
switch (state)
{
case AnimatorStates.WALK:
animName = "Walk";
break;
case AnimatorStates.RUN:
animName = "Run";
break;
}
if (_anim == null)
_anim = GetComponent<Animator>();
_anim.Play(animName);
}
void Update()
{
if (MyCommands.walkbetweenwaypoints == true)
{
PlayState(AnimatorStates.RUN);
}
else
{
PlayState(AnimatorStates.IDLE);
}
}
}
The code is working fine but i don't think it's right to call the PlayState so many times in the Update. I want that if it's RUN or IDLE state to call it only once in the Update function.
Once walkbetweenwaypoints is true or false it will call the state nonstop in the Update function.

Save the last state in Update method and only update if state has actually changed:
AnimatorStates lastState = AnimatorStates.IDLE;
public void PlayState(AnimatorStates state)
{
if(state != lastState)
{
string animName = string.Empty;
switch (state)
{
case AnimatorStates.WALK:
animName = "Walk";
break;
case AnimatorStates.RUN:
animName = "Run";
break;
}
if (_anim == null)
_anim = GetComponent<Animator>();
_anim.Play(animName);
lastState = state;
}
}

Related

Respawning enemies from other scenes in unity

Im probably go about this the wrong way, but i've hit a road block. In my game sitting at a souls like camp fire should respawn all enemies with a certain type on them and I cant think of a way to get this to work from other scenes when the player has rested.
I've created the respawning script but it only works when resting in the same room as the enemy.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyManager : MonoBehaviour
{
public GameObject enemyCore;
private Core core;
enum DeathType { respawnOnEnter, respawnOnRest, neverRespawn}
[SerializeField] DeathType deathType;
private Death Death => death ? death : core.GetCoreComponent(ref death);
private Death death;
private bool canRespawn = true;
private bool justRested = false;
private bool justRespawned = false;
public void Start()
{
core = enemyCore.GetComponent<Core>();
if (ES3.KeyExists(core.transform.parent.gameObject.name + "canRespawn"))
canRespawn = ES3.Load<bool>(core.transform.parent.gameObject.name + "canRespawn");
if (ES3.KeyExists("justRested"))
canRespawn = ES3.Load<bool>("justRested");
switch (deathType)
{
case DeathType.respawnOnEnter:
canRespawn = true;
break;
case DeathType.respawnOnRest:
if(justRested || justRespawned)
{
canRespawn = true;
justRespawned = false;
}
break;
}
ES3.Save(core.transform.parent.gameObject.name + "canRespawn", canRespawn);
if (!canRespawn)
{
core.transform.parent.gameObject.SetActive(false);
}
}
public void Rested()
{
justRested = true;
justRespawned = true;
ES3.Save("justRested", justRested);
}
public void Update()
{
if(Death.JustDied)
{
switch (deathType)
{
case DeathType.respawnOnEnter:
canRespawn = true;
break;
case DeathType.respawnOnRest:
canRespawn = false;
break;
case DeathType.neverRespawn:
canRespawn = false;
break;
}
ES3.Save(core.transform.parent.gameObject.name + "canRespawn", canRespawn);
Death.JustDied = false;
}
switch (deathType)
{
case DeathType.respawnOnRest:
if (justRested)
{
canRespawn = true;
justRested = false;
ES3.Save(core.transform.parent.gameObject.name + "canRespawn", canRespawn);
}
break;
}
}
private void OnEnable()
{
PlayerRestState.RestAction += Rested;
}
private void OnDisable()
{
PlayerRestState.RestAction -= Rested;
}
}
Heres the code I have written, the script should go on each enemy individually.
Thanks for the help.
Fixed by using scriptable objects meaning I could access data from any enemy from any scene. If anyone was wondering.

How can I check if a scene is loaded don't load it again?

Line number 53 in the else :
SceneManager.LoadScene(0, LoadSceneMode.Additive);
I want that if this scene 0 is already loaded with any other scene in my case there are two scenes only for now but if scene 0 the main menu is already loaded Additive then don't load it again when clicking the escape key.
This script sits in Game scene 1
The problem is if in the main menu I click for a new game and the game scene has loaded but before it removed unloaded the main menu scene and I click escape too fast it will load the main menu scene, again and again, all the time.
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Experimental.GlobalIllumination;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class BackToMainMenu : MonoBehaviour
{
public GameObject[] objsToDisable;
public AudioMixer audioMixer;
public static bool gameSceneLoaded;
public GameObject fadeImage;
public Light lights;
private float volumeLinearToDecibel;
private void Awake()
{
gameSceneLoaded = true;
}
// Start is called before the first frame update
void Start()
{
GetGameMusicVolume();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape))
{
if (Time.timeScale == 0)
{
lights.enabled = true;
DisableEnableUiTexts(true);
SceneManager.UnloadSceneAsync(0);
if (fadeImage != null)
fadeImage.SetActive(true);
GetGameMusicVolume();
Cursor.visible = false;
Time.timeScale = 1;
}
else
{
Time.timeScale = 0;
lights.enabled = false;
MenuController.LoadSceneForSavedGame = false;
SceneManager.LoadScene(0, LoadSceneMode.Additive);
SceneManager.sceneLoaded += SceneManager_sceneLoaded;
Cursor.visible = true;
}
}
}
private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1)
{
fadeImage = GameObject.FindWithTag("Game Scene Fader");
if (fadeImage != null)
fadeImage.SetActive(false);
audioMixer.SetFloat("gamemusicvolume", Mathf.Log(0.0001f) * 20);
DisableEnableUiTexts(false);
var pauseResumeMainMenuMode = FindInActiveObjectByName("MenuDefaultButtons_Canvas_Pause_Resume");
var newFreshGameMainMenuMode = FindInActiveObjectByName("MenuDefaultButtons_Canvas_NewFreshGame_SaveGame_Not_Exist");
newFreshGameMainMenuMode.SetActive(false);
pauseResumeMainMenuMode.SetActive(true);
SceneManager.sceneLoaded -= SceneManager_sceneLoaded;
}
private void DisableEnableUiTexts(bool enabled)
{
foreach (GameObject go in objsToDisable)
{
if (go.name == "Cameras Control")
{
foreach (Transform child in go.transform)
{
if (child.name == "Main Camera")
{
if (enabled == false)
{
child.GetComponent<Camera>().enabled = false;
}
else
{
child.GetComponent<Camera>().enabled = true;
}
}
}
}
else
{
go.SetActive(enabled);
}
}
}
private float LinearToDecibel(float linear)
{
float dB;
if (linear != 0)
dB = 20.0f * Mathf.Log10(linear);
else
dB = -144.0f;
return dB;
}
private void GetGameMusicVolume()
{
volumeLinearToDecibel = LinearToDecibel(PlayerPrefs.GetFloat("mainmenumusicvolume") / 100f);
audioMixer.SetFloat("gamemusicvolume", volumeLinearToDecibel);
}
GameObject FindInActiveObjectByName(string name)
{
Transform[] objs = Resources.FindObjectsOfTypeAll<Transform>() as Transform[];
for (int i = 0; i < objs.Length; i++)
{
if (objs[i].hideFlags == HideFlags.None)
{
if (objs[i].name == name)
{
return objs[i].gameObject;
}
}
}
return null;
}
}
You can check if the menu object cached already exist open the menu object, if not load the menu scene. Simple as that.
The more general way would be using SceneManager.GetSceneByBuildIndex
This method will return a valid Scene if a Scene has been added to the build settings at the given build index AND the Scene is loaded. If it has not been loaded yet the SceneManager cannot return a valid Scene.
so simply check IsValid like
if(SceneManager.GetSceneByBuildIndex(0).IsValid())

Despite the name of the file and the name of the class being the same, Unity still can't find the class

Despite the many times I have rewritten the file and made sure the class and the file name match up unity is still saying it can't find the script class. I even made sure all the spelling important words were correct, and it still says it can't find the script class. The name of the file is BasicAI.cs This is what the file looks like.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace UnityStandardAssets.Characters.ThirdPerson
{
public class BasicAI : MonoBehaviour
{
public UnityEngine.AI.NavMeshAgent agent;
public ThirdPersonCharacteracter character;
public enum State {
PATROL,
CHASE
}
public State state;
private bool alive;
//Patrolling
public GameObject[] markers;
private int markerID=0;
public float patrolSpeed=0.0f;
//Chasing
public float chaseSpeed=1f;
public GameObject target;
// Start is called before the first frame update
void Start()
{
agent = GetComponent<UnityEngine.AI.NavMeshAgent>();
character = GetComponent<ThirdPersoncharacter>();
agent.updatePosition = true;
agent.updateRotation = false;
state = BasicAI.State.PATROL;
alive = true;
StartCoroutine("FiniteState");
}
IEnumerator FiniteState()
{
while(alive)
{
switch(state)
{
case State.PATROL:
Patrol();
break;
case State.CHASE:
Chase();
break;
}
yield return null;
}
}
void Patrol()
{
agent.speed = patrolSpeed;
if(Vector3.Distance(this.transform.position, markers[markerID].transform.position) >= 2)
{
agent.setDestionation(markers[markerID].transform.position);
character.move(agent.desiredVelocity, false, false);
}
else if(Vector3.Distance(this.transform.position, markers[markerID].transform.position) <= 2)
{
markerID += 1;
if(markerID>markers.Length)
{
markerID = 0;
}
}
else
{
character.move(Vector3.zero, false, false);
}
}
void Chase()
{
agent.speed = chaseSpeed;
agent.setDestionation(target.transform.position);
character.move(agent.desiredVelocity, false, false);
}
void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
state = basicAI.State.CHASE;
target = other.GameObject;
}
}
}
}

(Unity2D) How to get the player to interact with a platform base on the color of their sprite?

First-time poster - long time browser.
I am creating a system, where the player can only collide with objects that has the same color as their paintbrush (child sprite). The system works well enough when the color and string of the object are set randomly, but when I attempt to implement the system using UI buttons - it just ignores the string change.
//VAR: Character Color
[Header("VAR Color Switch")]
public string currentColor;
public Color colorPrimary;
public Color colorSecondary;
public Color colorTertiary;
private GameObject child;
public SpriteRenderer sr;
private void Start() {
child = transform.GetChild(0).gameObject;
sr = child.GetComponent<SpriteRenderer>();
SetRandomColor();
}
private void OnCollisionEnter2D(Collision2D other) {
switch(other.gameObject.tag)
{
case "Jumppad":
jumpForce = jumpBoost;
break;
case "Ground":
jumpForce = initialJumpForce;
break;
case "Primary":
if(other.gameObject.tag == currentColor)
{
other.gameObject.GetComponent<Collider2D>().enabled = true;
}else if (other.gameObject.tag != currentColor)
{
other.gameObject.GetComponent<Collider2D>().enabled = false;
}
break;
case "Secondary":
if(other.gameObject.tag == currentColor)
{
other.gameObject.GetComponent<Collider2D>().enabled = true;
}else if (other.gameObject.tag != currentColor)
{
other.gameObject.GetComponent<Collider2D>().enabled = false;
}
break;
case "Tertiary":
if(other.gameObject.tag == currentColor)
{
other.gameObject.GetComponent<Collider2D>().enabled = true;
}else if (other.gameObject.tag != currentColor)
{
other.gameObject.GetComponent<Collider2D>().enabled = false;
}
break;
}
}
public void SetRandomColor()
{
int index = UnityEngine.Random.Range(0, 3);
switch (index)
{
case 0:
currentColor = "Primary";
sr.color = colorPrimary;
break;
case 1:
currentColor = "Secondary";
sr.color = colorSecondary;
break;
case 2:
currentColor = "Tertiary";
sr.color = colorTertiary;
break;
}
}
}
Code on UI buttons
PlayerController player;
// Start is called before the first frame update
void Start()
{
player = FindObjectOfType<PlayerController>();
}
public void SwitchPrimary()
{
player.currentColor = "Primary";
player.sr.color = player.colorPrimary;
}
public void SwitchSecondary()
{
player.currentColor = "Secondary";
player.sr.color = player.colorSecondary;
}
public void SwitchTertiary()
{
player.currentColor = "Teritary";
player.sr.color = player.colorTertiary;
}
I am genuinely unsure of what could be the problem. No matter what variation I attempt, I somehow get the same result.

UNET SyncVar on server side not updated

I am trying to use SyncVar but I do not fully understand what I am doing wrong, if I do it wrong that is. Here is the situation:
I have two public SyncVar's: redFunds and blueFunds and two local "old"-version to compare with
I initiate the SyncVar in Start by using Cmd_UpdateXxx, that works
I have two buttons, one for each SyncVar
In Update I compare the SyncVar vs. the oldXxxFunds. If a hit I display on scene
When I run the code it does display the correct numbers on the scen on both players (Red & Blue) but that is not fully reflected in the editor when looking at the "public" SyncVar's. When pressing the red button it only reflect, in the editor, on the red player not the Blue.
Can someone explain to me what i am doing wrong here? ...if I am doing something wrong that is. Shouldn't I see the changes in the editor as well?
[SyncVar] public int redFunds;
[SyncVar] public int blueFunds;
public int oldRedFunds;
public int oldBlueFunds;
private void Start ()
{
if (!isLocalPlayer)
return;
Cmd_UpdateRed (10);
Cmd_UpdateBlue (20);
}
// Button
void btn_Red ()
{
if (!hasAuthority)
return;
Cmd_UpdateRed (10000);
}
void btn_Blue ()
{
if (!hasAuthority)
return;
Cmd_UpdateBlue (20000);
}
[Command]
void Cmd_UpdateRed (int _value)
{
redFunds = _value;
}
[Command]
void Cmd_UpdateBlue (int _value)
{
blueFunds = _value;
}
void Update ()
{
if (redFunds != oldRedFunds)
{
txt_RedTotalFunds = GameObject.Find ("txt_RedTotalFunds").GetComponent<Text> ();
txt_RedTotalFunds.text = "$" + redFunds;
oldRedFunds = redFunds;
}
if (blueFunds != oldBlueFunds)
{
txt_BlueTotalFunds = GameObject.Find ("txt_BlueTotalFunds").GetComponent<Text> ();
txt_BlueTotalFunds.text = "$" + blueFunds;
oldBlueFunds = blueFunds;
}
}
The best way I can think is to use a GameRoomInfo as an network object owned by the server.
It can be done with 2 simple scripts as follows:
GameRoomInfo Script
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using System.Collections;
public class GameRoomInfo : NetworkBehaviour
{
public Text txt_RedTotalFunds;
public Text txt_BlueTotalFunds;
public int oldRedFunds = 0;
public int oldBlueFunds = 0;
[SyncVar]
public int redFunds;
[SyncVar]
public int blueFunds;
void Update()
{
if (redFunds != oldRedFunds)
{
//txt_RedTotalFunds = GameObject.Find("txt_RedTotalFunds").GetComponent<Text>();
txt_RedTotalFunds.text = "$" + redFunds;
Debug.Log("Red - $" + redFunds);
oldRedFunds = redFunds;
}
if (blueFunds != oldBlueFunds)
{
//txt_BlueTotalFunds = GameObject.Find("txt_BlueTotalFunds").GetComponent<Text>();
txt_BlueTotalFunds.text = "$" + blueFunds;
Debug.Log("Blue - $" + blueFunds);
oldBlueFunds = blueFunds;
}
}
}
Player Script
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using System.Collections;
public class Player : NetworkBehaviour
{
public enum PlayerColor
{
Red,
Blue
};
//used to update the right variables (Blue player updates blueFunds and Red player updates redFunds)
[SyncVar]
PlayerColor playerColor;
static int colorSelect = 0;
void Start()
{
if(isServer)
{
// the server selects the player color when created
switch(colorSelect % 2)
{
case 0:
playerColor = PlayerColor.Red;
break;
case 1:
playerColor = PlayerColor.Blue;
break;
}
colorSelect++;
}
}
void Update()
{
if (!isLocalPlayer)
return;
if (hasAuthority && Input.GetKeyDown(KeyCode.KeypadPlus))
{
Cmd_UpdateAdd(10);
}
}
// Button
void btn_Update()
{
if (!hasAuthority)
return;
Cmd_Update(0);
}
void btn_Add()
{
if (!hasAuthority)
return;
Cmd_Update(10);
}
[Command]
public void Cmd_Update(int _value)//The command updates the GameRoomInfo variables according to the player color
{
switch (playerColor)
{
case PlayerColor.Red:
FindObjectOfType<GameRoomInfo>().redFunds = _value;
break;
case PlayerColor.Blue:
FindObjectOfType<GameRoomInfo>().blueFunds = _value;
break;
default:
break;
}
}
[Command]
public void Cmd_UpdateAdd(int _value)
{
switch (playerColor)
{
case PlayerColor.Red:
FindObjectOfType<GameRoomInfo>().redFunds += _value;
break;
case PlayerColor.Blue:
FindObjectOfType<GameRoomInfo>().blueFunds += _value;
break;
default:
break;
}
}
}
I have made some small changes to help testing. Feel free to adapt to your needs.

Categories