Calling all scripts with the Interface, not just a single one - c#

I have a few scripts which all inherit from an Interface I have called IPlayer. The goal of this interface is to register Damage, Experience, and LevelUp.
public interface IPlayer {
void TakeDamage(int damage);
void GiveExperience(int experience);
void LevelUp();
}
For example, 2 scripts at the moment which inherit from IPlayer:
PlayerStatus:
public void GiveExperience(int experience)
{
currentExperience += experience;
if (currentExperience >= maxExperience)
{
LevelUp();
return;
}
UpdateUI();
}
public void LevelUp()
{
print("Player has Leveled up!");
maxExperience *= 2;
if (maxExperience >= 2000)
{
return;
}
currentExperience = 0;
currentHealth = playerStats.maxHealth;
vitTime = (-1 * Mathf.Sqrt(playerStats.vit / 130) + 1) / 2;
currentMana = playerStats.maxMana;
wisTime = (-1 * Mathf.Sqrt(playerStats.wis / 130) + 1) / 2;
UpdateUI();
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
if (currentHealth <= 0)
{
currentHealth = 0;
//UpdateUI(); /// MAYBE NEEDED LATER WHEN Death() actually does something.
Death();
}
UpdateUI();
}
PlayerStats:
public void LevelUp()
{
maxHealth += Random.Range(8, 16);
maxMana += Random.Range(8, 16);
level++;
uiStats.UpdateUI();
}
public void TakeDamage(int dmage)
{
}
public void GiveExperience(int exp)
{
}
Now the problem is, that in PlayMode, nothing actually happens, the health doesnt go down, experience doesnt go up, and ofcourse no levels are gained.
My first inclination is that when this is called for example:
IPlayer Iplayer = collision.transform.GetComponent<IPlayer>();
if (Iplayer != null)
{
Iplayer.TakeDamage(damage);
}
It will find the first script which inherits from IPlayer, and call that Method(TakeDamage). Instead I want it to find ALL scrips that inherit from IPlayer(scr_PlayerStatus, scr_PlayerStats, and more scripts that ALL inherit from IPlayer, and call ALL IPlayer methods on ALL Scripts attached to that gameobject, not just the first one it finds.
So my player basically has multiple scripts that all inherit from IPlayer, and I want them ALL to execute, when the interface is called.
I hope my explanation is clear and the code can hopefully paint a picture aswell.
BTW, I know this works when I use multiple Interfaces, one IDamage, IExperience and ILevelUp, but now I combined those 3 interfaces into one to make the code cleaner and easier to read, but that borked it unfortunately. There are no errors, nullreferences or anything like that, really I am convinced that the interface is only calling the first script it finds and not all of them attached to the GameObject.

use foreachmethod.
IPlayer[] Iplayers = collision.transform.GetComponents<IPlayer>();
if (Iplayers != null)
{
foreach (IPlayer iplayer in Iplayers)
{
iplayer.TakeDamage(damage);
}
}

Related

Passing reference to a variable in another object

I am trying to learn unity. This is something I would use in Javascript so I was hoping to find a way to do this in C#, where you take the collision variable and use the reference to have another function clean up the variables after you are finished.
Edit
I have been practicing since posting this, I am trying to pass a reference to another class to handle the turn on and off of the variable.
private void OnTriggerEnter(Collider collision) {
if(collision.gameObject.CompareTag("Enemy")) {
PowerUpRoutine(ref collision.gameObject.GetComponent<enemy>().powerup);
Destroy(gameObject);
}
if (collision.gameObject.CompareTag("Player")) {
PowerUpRoutine(ref collision.gameObject.GetComponent<player>().powerup);
Destroy(gameObject);
}
}
IEnumerator PowerUpRoutine(float target) {
target = 1;
yield return new WaitForSeconds(5);
target = 0;
}
I have also tried not passing powerup, but just the object and it still errors. Is there anyway to accomplish this?
Original
private void OnTriggerEnter(Collider collision) {
if(collision.gameObject.CompareTag("Enemy")) {
collision.gameObject.GetComponent<enemy>().powerup = 1;
StartCoroutine(removePower(collision.gameObject.GetComponent<enemy>()));
Destroy(gameObject);
}
if (collision.gameObject.CompareTag("Player")) {
collision.gameObject.GetComponent<player>().powerup = 1;
StartCoroutine(removePower(collision.gameObject.GetComponent<player>()));
Destroy(gameObject);
}
}
IEnumerator removePower(GameObject target) {
yield return new WaitForSeconds(5);
target.powerup = 0;
}
First if all there is a general flaw in your approach: When you
Destroy(gameObject);
this object this component is attached to then also all Coroutines are immediately canceled.
I would therefore start the coroutine rather on the target itself (see example below).
And then your classes should have a common interface or base class like for example
Solution 1 - Common Base Class
public abstract class Character : MonoBehaviour
{
public float powerup;
// And other common members
}
And then you inherit
public class Player : Character
{
// Additional player specific stuff
}
and
public class Enemy : Character
{
// Additional enemy specific stuff
}
Solution 2 - Common Interface
if you rather want to go for an interface
public interface ICharacter
{
float powerup { get; set; }
}
And then both your classes have to implement that
public class Player : MonoBehaviour, ICharacter
{
public float powerup { get; set; }
// Additional player specific stuff
}
and
public class Enemy : MonoBehaviour, ICharacter
{
public float powerup { get; set; }
// Additional player specific stuff
}
And then your collision code could simply be
private void OnTriggerEnter(Collider collision)
{
// TryGetComponent now finds anything inherited from Character
// you don't even need to check the tags
if(collision.TryGetComponent<Character>(out var character))
// Or if using the interface
//if(collision.TryGetComponent<ICharacter>(out var character)
{
// Let the character run this coroutine without even having to know what it does
character.StartCoroutine(PowerUpRoutine(character));
// This is now ok and not terminating the Coroutine since the routine is run by character itself
Destroy(gameObject);
}
}
IEnumerator PowerUpRoutine(Character target)
// or if using the interface accordingly
//IEnumerator PowerUpRoutine(ICharacter target)
{
// Have in mind though that this routine is now running on a different object
// at a time where this component is already destroyed => You can't reference to anything of this component in here!
target.powerup = 1;
yield return new WaitForSeconds(5);
target.powerup = 0;
}
Solution 3 - Simple Overload
Or as last resort if the before two is not an option for whatever reason you could of course also simply have two overloads
IEnumerator PowerUpRoutine(Player target)
{
target.powerup = 1;
yield return new WaitForSeconds(5);
target.powerup = 0;
}
IEnumerator PowerUpRoutine(Enemy target)
{
target.powerup = 1;
yield return new WaitForSeconds(5);
target.powerup = 0;
}
and then do
private void OnTriggerEnter(Collider collision)
{
if(collision.TryGetComponent<Player>(out var player))
{
player.StartCoroutine(PowerUpRoutine(player));
Destroy(gameObject);
}
else if(collision.TryGetComponent<Enemy>(out var enemy))
{
enemy.StartCoroutine(PowerUpRoutine(enemy));
Destroy(gameObject);
}
}
Note: Coroutines are also stopped when the MonoBehaviour is destroyed or if the GameObject the MonoBehaviour is attached to is disabled.
You should destroy the gameobject when the coroutine finishes.
StartCoroutine(removePower(collision.gameObject.GetComponent<enemy>()));
//Destroy(gameObject);
IEnumerator removePower(GameObject target) {
yield return new WaitForSeconds(5);
target.powerup = 0;
Destroy(gameObject);
}
Or run the coroutine on another gameobject
var enemy = collision.gameObject.GetComponent<enemy>();
enemy.StartCoroutine(removePower(enemy));

My value keeps on getting set to 1 in unity

So I have been trying to get a difficulty system to work in my game but I have encountered a problem, my float keeps on getting reset to 1, I have the larger script on a prefab that is in another prefab and the small script is one I have on the "DifficultyController". I have tried looking through the files but I cannot find any instance of the number 1 I have also tried making a new prefab to see if that was the problem but still the number 1!
The script in the prefab:
public float health;
public GameObject deathEffect;
public float difficulty;
private float destroy;
private void Update()
{
if (health <= 0)
{
//Instantiate(deathEffect, transform.position, Quaternion.identity);
Destroy(gameObject);
}
}
private void Start()
{
EnemyDifficulty(difficulty);
}
public void TakeDamage (float damage)
{
health -= damage;
}
public void EnemyDifficulty(float chance)
{
Debug.Log(chance);
destroy = Random.Range(0, 50);
if (destroy <= 3) { Destroy(gameObject); }
chance = Random.Range(chance / 2, chance * 2);
Debug.Log(chance);
if (chance <= 8 && chance >= 3)
{
BasicEnemy();
}
if (chance <= 20 && chance >= 8)
{
MediumEnemy();
}
if(chance <= 3)
{
Destroy(gameObject);
Debug.Log("Destroeyed");
}
}
public void BasicEnemy()
{
Debug.Log("basic");
}
public void MediumEnemy()
{
Debug.Log("medium");
}
}
Code in the "DifficultyController"
public Enemy enemy;
public float difficultye = 5;
// Update is called once per frame
void Update()
{
enemy.difficulty = difficultye;
}
You haven't specified which variable keeps getting set to one, but I assume it's either health, difficulty, or difficultye. This is because they are public, meaning that they can be edited in the inspector. If the inspector value differs from the code value, then the inspector value will override that of our code.
You can fix this by changing the variables in editor, marking them as [System.NonSerialized], or setting them to private, depending on which best fits your needs.

Unable to add a number to a variable (New to Unity)

still reasonably new to Unity and c# and so this may be a small and insignificant question to some but it has stumped me. I'm trying to add a number to my score when a game object is destroyed, I have a target script attached to a target prefab.
using UnityEngine;
public class Target : MonoBehaviour
{
public float health = 50f;
private float TotalCubesHit = 0;
public void TakeDamage (float amount){
health = health - (amount);
if (health <= 0f) {
TotalCubesHit += 1;
Die();
}
}
public void Die()
{
Destroy(gameObject);
Debug.Log(TotalCubesHit);
}
}
For some reason, it never gets above one and I'm unsure as to why? any help would be appreciated
Simplest way would be to use a static counter. This makes it a class field which is "shared" among all instances or better said is simply not bound to any instance:
public class Target : MonoBehaviour
{
public float health = 50f;
private static float totalCubesHit = 0;
// Optionally add a public ReadOnly property
// for allowing external access
public static float TotalCubesHit => totalCubesHit;
public void TakeDamage (float amount)
{
health -= amount;
if (health <= 0f)
{
totalCubesHit++;
Die();
}
}
public void Die()
{
Destroy(gameObject);
Debug.Log(totalCubesHit);
}
}
A useful side-effect (and therefore I added the public ReadOnly property): You can now also access it from any other script by simply using
Target.TotalCubeHits
while being sure it can not be changed by other scripts.
It is because it is incremented only once .. thats right before it dies.
public void TakeDamage (float amount){
health = health - (amount);
TotalCubesHit += 1; // <-- Move that outside of the if statement
if (health <= 0f) {
Die();
}
}
**EDIT: I have been re-reading your question and one thing i dont quite follow is: "I'm trying to add a number to my score when a game object is destroyed". If your intent is to keep track of the totalCubeHits from different Target objects that are destroyed, this wont work unless you are re-using the same Target object. If you create a new instance each time, TotalCubeHits will always start from 0.
Store the TotalCubeHits outside or initialize it with a number from previous object.

In Unity, what type of method is Update()? And how to make my own?

How do gameobjects know that void Update means every frame and that OnCollisionEnter means when they collide. Can I make my own? Kind of like this
void OnPositionChange () {
//code goes here
}
And then any script that has that OnPositionChange will recognize it and do something with it when the position changes.
The BuiltIn SendMessage component:
http://docs.unity3d.com/ScriptReference/Component.SendMessage.html
And this might help you for something, I guess:
http://answers.unity3d.com/questions/676625/creating-my-own-custom-unity-messages.html
Here, the suggestion is to make a base class from which all objects that will need the method extends. In the example he used a damage source and damageable objects and example how they would interact:
In DamageSource.cs (the class you'd use to give damage to )
using UnityEngine;
using System.Collections;
public class DamageSource : MonoBehaviour {
protected float damageAmount = 10f;
//not 100% necessary, but handy for an example of how to
//handle damage based on the attacker (which is
//relevant for info sent in the OnTakeDamage() method
protected ElementType elementType = ElementType.Normal;
//we use a function for getting the damage this
//DamageSource can do because it lets us overwrite it.
//Eg, if the enemy is weakened, you can factor that
//in and return a lesser amount of damage.
public float GetDamageAmount() {
return damageAmount;
}
public ElementType GetElementType() {
return elementType;
}
}
//kinds of elements available for damage / resistance calculations
public enum ElementType {
Normal,
Fire,
Ice,
Lightning
}
In DamageableObject.cs (the base class from which all damageable objects inherit):
using UnityEngine;
using System.Collections;
public class DamageableObject : MonoBehaviour {
protected bool wasRecentlyHit;
protected float health;
protected float maxHealth;
public void Awake() {
health = maxHealth;
}
//Creating a virtual void method lets you choose whether
//or not you want to set it in a derived class.
//Here, we track the amount of damage and the source
//the damage came from. This can sometimes be handy for
//context-sensitive reactions to being damaged. Eg, play
//a particular sound in damaging the player, when
//successfully damaged by a particular attack.
//Note that this base implementation does nothing - you
//override it in an inheriting class, very similar to using Update() etc.
protected virtual void OnTakeDamage(float damageAmount, DamageSource damageSource) {}
//An example of how you'd check whether damage is incoming.
//You can alternatively just call
//someDamageableObject.TryDoDamage() from another script.
public void OnTriggerEnter(Collider other) {
DamageSource damageGiver = other.GetComponent<DamageSource>();
if (damageGiver) {
TryDoDamage(damageGiver.GetDamageAmount(),damageGiver.gameObject);
}
}
public void TryDoDamage(float damageAmount, GameObject damageGiver) {
//early out, this DamageableObject was damaged a very
//short time ago and shouldn't be damaged again so soon
if (wasRecentlyHit) return;
//optionally perform any damage calculations here based
//on the damageGiver, eg more damage from the player
//being weakened somehow, or less damage from type
//resistances... etc.
damageAmount = CalculateDamage(damageAmount,damageGiver);
//if after our damage calculations we still have an
//amount of damage greater than 0, we do the damage and
//send the OnTakeDamage() message.
if (damageAmount>0f) {
health -= damageAmount;
//optional handling of dying (uncomment this and the OnDeath() function to enable)
//if (health<0f) {
// OnDeath(damageAmount,damageGiver);
//}
//else {
OnTakeDamage(damageAmount,damageGiver);
//}
}
}
//Uncomment this and the (healtn<0f) if statement above
//if you want to handle dying as well as being damaged
//protected virtual void OnDeath(float damageAmount, DamageSource damageSource);
//Default implementation for calculating damage,
//given some amount of damage, and some source of damage.
//Override this in an inheriting class if you want to do
//different damage, eg based on the damage source (2x
//damage from fire attacks, 0.5x damage from ice
//attacks... etc) or based on the DamageableObject's
//current state (eg, player is weakened, so takes 1.5x damage)
protected float CalculateDamage(float damageAmount, DamageSource damageSource) {
return damageAmount;
}
}
In PlayerDamageReceiver.cs:
using UnityEngine;
using System.Collections;
public class PlayerDamageReceiver : DamageableObject {
//override the OnTakeDamage() method to make a
//different implementation of it for this class
protected override void OnTakeDamage(float damageAmount, DamageSource damageSource) {
Debug.Log("Ouch, the player was damaged!");
}
//Uncomment this to override the OnDeath() function
//in DamageableObject (if you've uncommented that, that is)
//protected override void OnDeath(float damageAmount, DamageSource damageSource) {
// Debug.Log("Uhoh... The player died. :(");
//}
//override the CalculateDamage() function to
//determine how damage applies to the player
protected override float CalculateDamage(float damageAmount, DamageSource damageSource) {
//Example: give the player a 2x weakness to fire damage, and immunity to ice damage
switch (damageSource.GetElementType()) {
case (ElementType.Fire):
damageAmount *= 2f;
break;
case (ElementType.Ice):
damageAmount = 0f;
break;
}
return damageAmount;
}
}
(I copied and paste the code from "nesis" user here, as asked by a fellow on comments)
Actually you can do that. Simply you can make your own Behavior class.
public class MyBehavior : MonoBehaviour {
Vector3 lastPosition = new Vector3();
void Update () {
Vector3 position = new Vector3();
if (position != lastPosition)
{
OnPositionChange();
lastPosition = position;
}
else
{
lastPosition = position;
}
}
public virtual void OnPositionChange(){}
}
Now you define your script which inherits MyBehavior instead of MonoBehavior directly.
public class test : MyBehavior {
public override void OnPositionChange()
{
Debug.Log("override");
}
}
This is the closest thing to what you asked I belive.
float startPositionX;
float startPositionY;
float startPositionZ;
float currentPositionX;
float currentPositionY;
float currentPositionZ;
void start(){
startPositionX = gameObject.transform.position.x ;
startPositionY = gameObject.transform.position.y ;
startPositionz = gameObject.transform.position.z ;
}
void update() {
currentPositionX = gameObject.transform.position.x ;
currentPositionY = gameObject.transform.position.y ;
currentPositionZ = gameObject.transform.position.z ;
OnPositionChange ();
}
void OnPositionChange () {
if(startPositionX == ! currentPositionX ||
startPositionY == ! currentPositionY ||
startPositionZ == ! currentPositionZ){
/*write your own code that you want to perform.*/ }
}
Update(), Start() and other method with special function like these, are overloads from MonoBehaviour class. You can see all of your scripts are derived from MonoBehaviour class.
What you need is using Events. You can create an event and delegate and on special situation raise them so all your scripts that are register in that event can call their own method for that event.

Functions that call functions and/or delegates

Let's say I have an Enemy class with a couple properties and a method AddPoints which will add experience points to an Enemy object. After a certain amount of experience points the Level property of the Enemy object will increase.
Initially I thought 'how can I make the program update the Level property when I don't know when the correct amount of experience points will be reached?. This made me think of events (have to listen for the event to occur, in this case the outcome of the LevelUp() method) so I decided to do something like
private void LevelUp()
{
if (ExperiencePoints > (5 * Level))
{
Level++;
}
}
public void AddPoints(int points)
{
this.ExperiencePoints += points;
LevelUp();
}
This way every time there are points added to the Enemy object the method will check whether or not the Level property needs to be incremented. Having one method call another method made think about containment/delegation (one method is 'nested' inside another). In this way, my AddPoints function sort of acts like a function pointer (at least in my mind).
Does anyone with a knowledge of language design or a good historical knowledge of C++/C# find this a helpful way of thinking about delegates? With the following code is there any way that a delegate can improve the program, or is it too simple?
full Enemy class
class Enemy
{
public int ExperiencePoints { get; set; }
public string Name { get; set; }
private int level;
public int Level
{
get { return level; }
private set { level = value; }
}
private void LevelUp()
{
if (ExperiencePoints > (5 * Level))
{
Level++;
}
}
public void AddPoints(int points)
{
this.ExperiencePoints += points;
LevelUp();
}
public Enemy()
{
ExperiencePoints = 1;
Level = 1;
}
}
testing
delegate void myDelegate(int x);
class Program
{
static void Main(string[] args)
{
Enemy e = new Enemy();
myDelegate del = e.AddPoints;
e.AddPoints(10); //Level =1 at runtime, after this call Level=2
del(20);//now Level=3
Console.WriteLine(e.Level);//output = 3
}
}

Categories

Resources