Character not attacking via Unity - c#

So I have built an attack using SpriteFactory, and simply want to assign the keyboard letter A as a default attack. I have used GetKeyUp for the sole purpose of the character to attack once, and not multiple times like a loop (i.e. GetKeyDown). At this stage I have not included any enemies or anything, as i just want the the character to simply attack when I Press A. I have Included the game object and added the Attack.cs but no success. Maybe I am complete missing the point of what I am doing, but some help in the right direction would be appreciated.
using UnityEngine;
using System.Collections;
public class Attack : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(Input.GetKeyUp(KeyCode.A)) {
}
}
}
Ok so i have tried the below method. Have adjusted the code as follows:
using UnityEngine;
using System.Collections;
using FactorySprite = SpriteFactory.Sprite;
public class Attack : MonoBehaviour {
// you forgot to set name of variable representing your sprite
private FactorySprite sprite;
// Use this for initialization
void Start () {
sprite = GetComponent<FactorySprite> (); // Edited
}
void Update ()
{
if(Input.GetKeyUp(KeyCode.A))
{
sprite.Play("Attack");
}
}
}
But now have a ''nullReferenceError'' Object reference not set to an instant object?

You have to add yourSprite.Play("AnimationName"); to your loop:
void Update ()
{
if(Input.GetKeyUp(KeyCode.A))
{
yourSprite.Play("AnimationName");
}
}
For this to work you have to add a reference to your sprite to the script, with GetComponent().
Btw, GetKeyDown only triggers once for each key-press, you can use this too. (GetKey() would fire every frame)

Related

How to change the value of field simultaneously in two classes without using "static"? C#. Unity

I have two classes Player and PlayerUI in Player class I have its 'health' value and in PlayerUI class I have a Slider which value updates each frame according to 'health' value in Player class. I also have a Damage method that changes 'health' value. How can I change the value of field 'health' simultaneously in PlayerUI class and in Player class without using static?
Player class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public static float health = 150;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
Damage();
}
}
void Damage()
{
health -= 10;
}
}
PlayerUI class:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerUI : MonoBehaviour
{
public Slider hpBar;
private void Awake()
{
hpBar.maxValue = Player.health;
}
private void Update()
{
hpBar.value = Player.health;
}
}
In general you don't want to poll and set values in Update every frame but rather make your code more event driven => Only change the UI slider in the moment the value is actually changed.
I would use a property to make it event driven.
The main questions with this kind of constructs are: "Who shall be responsible for what?" and "Who shall know who?"
Option A
For example: Shall the player actively control the UI, without the UI knowing that a Player even exists?
The player "knows" the PlayerUI => it has its reference
The player actively informs the UI and updated the slider value
like e.g.
public class Player : MonoBehaviour
{
// In this case the Player needs to know the UI
public PlayerUI ui;
private float health = 150;
public float Health
{
get => health;
set
{
health = value;
// Whenever the value of Health is changed actively update the UI
ui.hpBar.value = value;
}
}
private void Start ()
{
// initially inform the UI
Health = health;
}
...
void Damage()
{
// Important: don't change the field anymore but rather the property!
Health -= 10;
}
}
Advantage
Player gains more power and responsibility and possibly becomes the core component that ensures a clean interoperability between multiple subcomponents like the UI and others. It would also be the central API point for accessing these sub components from the outside
Disadvantage
As the player needs to know all the subcomponents, everytime you add one you have to hard code it into the class and configure the references and connections between the subcomponents
Option 2
Or should it rather be the other way round and the Player doesn't even know that a UI exists?
-> Instead of knowing the UI you could add a UnityEvent (just like the button onClick) in befores code example where you can attach callbacks via the Inspector or on runtime to react to every change of the health property.
The Player simply invokes his event, not knowing/caring who is listening to it
The UI hooks up to the event -> The UI knows the player
like e.g.
public class Player : MonoBehaviour
{
// In this case the Player doesn't know anyone
// attach listeners via the Inspector or on runtime via code
public UnityEvent<float> OnHealthChanged;
private float health = 150;
public float Health
{
get => health;
set
{
health = value;
// Whenever the value of Health is changed just raise the event
// you don't care who is listening or not, but whoever is will get informed
OnHealthChanged.Invoke(value);
}
}
private void Start ()
{
// initially inform all listeners
// Note that for timing reasons it is essential that the listeners are attached in "Awake"
// so before this "Start" is called
// Alternatively the listeners can of course also ONCE poll the value directly -> Up to you
Health = health;
}
...
void Damage()
{
// Important: don't change the field anymore but rather the property!
Health -= 10;
}
}
The UI can hook up to the player and listen to the event and react to it like e.g.
public class PlayerUI : MonoBehaviour
{
...
// In this case the UI needs to know the player
public Player player;
private void Awake ()
{
player.OnHealthChanged.AddListener(UpdateSlider);
}
private void OnDestroy ()
{
if(player) player.OnHealthChanged.RemoveListener(UpdateSlider);
}
private void UpdateSlider (float value)
{
hpBar.value = value;
}
}
Advantage
This solves exactly the Disadvantage of Option A and allows to extremely flexible add and remove listeners without the Player having to care at all.
Disadvantage
With a lot of subcomponents and events and listeners it can quick get out of hand and become hard to debug and maintain. Also as noted this is more open for race conditions and timing and order issues.
Imo the best approach would be to create an event like "OnPlayerDamaged" in Player script that is raised when player gets any damage. Then create a method in PlayerUI that is subscribed to OnPlayerDamaged event and changes your healthbar.
Player script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public static event Action<float> OnPlayerDamaged;
private float health = 150;
private void Update()
{
if (Input.GetKeyDown(KeyCode.Q))
{
Damage();
}
}
void Damage()
{
health -= 10;
OnPlayerDamaged.Invoke(health)
}
public static float GetHealth()
{
return health;
}
}
PlayerUI
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlayerUI : MonoBehaviour
{
public Slider hpBar;
private void Awake()
{
hpBar.maxValue = Player.GetHealth();
}
private void OnEnable()
{
Player.OnPlayerDamaged += ChangeHealth;
}
private void OnDisable()
{
Player.OnPlayerDamaged -= ChangeHealth;
}
private void Update()
{
}
public void ChangeHealth(float currentHealth)
{
hpBar.value = currentHealth;
}
}
With this approach you can later add anything you want that uses the information about player getting damaged like screen effects or audio change. Just add the method to the event just like the ChangeHealth() method in our case. Remember to remove the method from the event on script disable to avoid multiple subscription of the same method.
GetHealth() method is a quick hack there but you should use ScriptableObject for player statistics like health and reference it where you want to use it. Then you don't need to pass currentHealth in the event and when it is raised just get value from scriptable object.

TextMesh Pro text will not change via script

I cannot seem to change my TextMeshPro value via script.
In my inspector I have a TextmeshPro object named Countdown. I have a script named GameController which is attached to this.
My script then sets the string value of Countdown to Hello but it does not work.
GameController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class GameController : MonoBehaviour {
public TextMeshProUGUI Countdown;
// Use this for initialization
void Start () {
Countdown = GetComponent<TextMeshProUGUI> ();
Countdown.text = "Hello";
}
// Update is called once per frame
void Update () {
}
}
In the inspector there is a field for TextMesh but I cannot drag the CountDown object to this for some reason, could that be the issue?
the problem is that you are using a regular TextMeshPro object, and in your code your looking for a TextMeshProUGUI, simple mistake. change code to:
public class GameController : MonoBehaviour {
public TextMeshPro Countdown;
// Use this for initialization
void Start () {
//you shouldnt need to get component the editor should take care of this for you when
//you drop it since you have the object set to TextMeshPro and not just GameObject
Countdown = GetComponent<TextMeshPro> ();
Countdown.text = "Hello";
}
// Update is called once per frame
void Update () {
}
}
the only way to make a TextMeshProUGUI object is to add it through a canvas. in your scene when you just add a TMP it will be Regular TMP which your "countdown" is. you can tell because it uses the TMP script not the TMPUGUI script.

Go to next level when Key is grabbed by Player Unity 2d

I need some help with a feature I'm implementing for a game made in Unity 2D.
The player must take a key in order to unlock a door (maybe showing an animation) and when the player go in front of that door if he has the key, he will automatically go to the next level.
I need help, cause the door is not letting go to the next level.
Here is the KEY code/script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class KeyScript : MonoBehaviour {
//public AudioSource coinSoundEffect;
public AudioClip key1;
void Awake () {
//source = GetComponent<AudioSource>();
}
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnCollisionEnter2D (Collision2D other) {
Debug.Log("Chiave Presa");
if(other.gameObject.tag =="Player")
GameObject.Find("KeyDoor").SendMessage("HitKey");
SoundManager2D.playOneShotSound(key1);
}
}
Here is the DOOR code/script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class DoorScript : MonoBehaviour {
public bool key = false;
public string nextLevelName;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void HitKey (){
Debug.Log("La porta รจ sbloccata");
key = true;
Destroy (GameObject.Find ("Key"));
}
void OnCollisionEnter2D (Collision2D other){
if(other.gameObject.tag == "Player"){
if (key == true){
Application.LoadLevel(nextLevelName);
//Destroy(gameObject);
//GameObject.Find("Key").SendMessage("DestroyKey"); //If you also want to destroy the key
//GoToNextLevel ();
}
}
}
void OnTriggerEnter2D (Collision2D other)
{
Application.LoadLevel(nextLevelName);
}
public virtual void GoToNextLevel()
{
//loadingImage.SetActive(true);
Application.LoadLevel(nextLevelName);
}
}
The code works but when the player goes in front of the door, he is not passing to the next level.
Any help or hint appreciated.
Cheers
First of all, you're missing one thing:
unity is Component based engine
This means you should make interactions between Components, not GameObjects.
In your script there's a public field public bool key which should not be accessible from the outside. But if you're looking for simple and wrong answer that will work then you can just replace this line :
GameObject.Find("KeyDoor").SendMessage("HitKey");
Into this one :
GameObject.Find("KeyDoor").GetComponent<DoorScript>().key = true;
In that case you'll end up with messy code and unstable game. What I can recommend to you as an good alternative is to rewrite your logic a bit.
Instead of making a MonoBehaviour which is not needed you can just create a Component :
public class KeyComponent : Component
{
[SerializeField]
AudioClip m_KeyPickupSound;
[SerializeField]
bool m_IsPickedUp;
public bool PickedUp
{
get { return m_IsPickedUp; }
}
public void PickUp()
{
m_IsPickedUp = true;
SoundManager2D.playOneShotSound(m_KeyPickupSound);
}
}
Now attach this into your Player's Components list and in your door script do:
void OnCollisionEnter2D (Collision2D other)
{
if(other.GetComponent<KeyComponent>() != null && other.GetComponent<KeyComponent>().PickedUp)
{
SceneManager.LoadScene(2);
}
}
Now only thing left is to update your Player's MonoBehaviour and add simple collision check :
void OnCollisionEnter2D(Collision2D other)
{
if(other.tag == "TAG_FOR_KEY")
GetComponent<DoorScript>().PickUp();
}
Now you're interacting with Components and not GameObject which then require less effort changing some scripts.
First off, you should change Application.LoadLevel to SceneManager.LoadScene. The first is obsolete in the newer versions of unity.
Second you need to make sure that your scenes are actually registered in File -> BuildSettings and the parameter you pass to LoadScene matches either the index in that list or the name as string.
Make sure your player needs to have a Rigidbody2D and a Collider2D and the door has a Collider2D component
If your doors Collider2D is a trigger you must use void OnTriggerEnter2D(Collider2D other).
Another problem may be that "Level 2" isn't added to your build settings, make sure that it is added to the level's list.
Answering your last comment as it seemed the problem was that the Collider2D on the door, you can make this in a lot of ways, the easiest way is this:
Add a public string variable called nextLevelName in your Door's script, then when calling LoadLevel, use this variable. You can change the value from the inspector in each level. The problem with this is that if you rearrange the levels then you need to change the strings in each level.
The best solution in that case is this one:
int currentLevelNum = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentLevelNum+1);
This way if you have your levels in order in your build settings you don't need to do anything else.

Enable/disable a GameObject component from a script [Unity3D]

I need to take the value of a boolean (put in a variable called "bouclier") set in one script to enable or disable a GameObject.
The variable is in game object Player (bottom right here):
And I need to enable of disable this game object ("Bouclier01"):
To do this, I attached a script to game object "Bouclier01". Here it is:
using UnityEngine;
using System.Collections;
public class ShowBouclier : MonoBehaviour {
public GameObject Bouclier01;
public bool bouclier;
// Use this for initialization
void Start () {
Bouclier01 = Bouclier01.GetComponent<GameObject>();
}
// Update is called once per frame
void Update () {
Bouclier01.enabled = false;
if (bouclier == true) {
Bouclier01.enabled = true;
}
}
}
I must be missing something, because this comes up with this error message:
Any idea how to properly accomplish this?
You can use GameObject.SetActive() function to activate or deactivate a GameObject (I think GameObject.enabled was in the old API):
Bouclier.SetActive(false);
By the way, if you want to know the current activation state of a GameObject, use GameObject.activeSelf, which is a read only variable:
Debug.Log(Bouclier.activeSelf);
it will works
public GameObject otherobj;//your other object
public string scr;// your secound script name
void Start () {
(otherobj. GetComponent(scr) as MonoBehaviour).enabled = false;
}

Acessing custom property with AddComponent

In Unity3D I've got a script that adds the variable 'eaten' as a component.
using UnityEngine;
[AddComponentMenu("My game/IsEaten")]
public class IsEaten : MonoBehaviour
{
public bool Eaten;
}
Yay! I can then add another script to access 'Eaten'
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
private Eaten someScript;
// Use this for initialization
void Start ()
{
someScript = GetComponent<IsEaten>();
bool temp = someScript.Eaten;
print(temp); // false
}
}
Which works fine. What do I have to do to access the variable with dot notation from another script? ie
if (myCube.eaten == true)
{
// do something
}
You know, in Unity one does rarely create the whole script to add a single property to some object. The common approach is to think of scripts as 'components' (which they are, in fact). Let me explain this, a component is a single piece of code that add certain functionality to your GameObject, like ability to animate, or to behave the laws of physics. So, maybe, it would be better to reform your IsEaten class to form a true component, like Pickup (I'm assuming that you need Eaten property for the pickup of some sort) that will have functionality to be eaten by a player, or something.
// You will actually need to attach a collider (and check the 'IsTrigger' checkbox on it) to your GameObject
// Then this method will be called whenewer some game object with a collider (e. g. player, npc's, monsters) will enter the trigger of this object
void OnTriggerEnter(Collider other)
{
// Check whether this object was eaten before and if the actor entering our trigger is actually the player
if (!Eaten && other.tag == "Player")
{
// Make shure that we will not get eaten twice
Eaten = true;
// Apply some effect to the player that has devoured us
other.GetComponent<Player>().AddHp(25);
}
}
Other than that, I'm personally thinking, that getting out of your way to simply enable a little sweeter syntax is not worth the hassle, but, if you provide some insight of what are you actually trying to implement, I may try to help you with it :)
One way to do it might be using Get/Set:
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour
{
private Eaten someScript;
// start new!
public bool eaten
{
get
{
return someScript.Eaten;
}
set
{
someScript.Eaten = value;
}
}
// end new!
// Use this for initialization
void Start ()
{
someScript = GetComponent<IsEaten>();
bool temp = someScript.Eaten;
print(temp); // false
}
}
Then you can access the Test class property:
Test t = GetComponent<Test>();
t.eaten = true;
ath.

Categories