I'm trying to call this method from my script enemy to my script player can anyone guide me?
The first namespace of code is enemy second is player
private void OnTriggerEnter2D(Collider2D other)
{
DamageDealer damageDealer = other.gameObject.GetComponent<DamageDealer>();
ProcessHit(damageDealer);
}
public void ProcessHit(DamageDealer damageDealer)
{
health -= damageDealer.GetDamage();
if (health <= 0)
{
Destroy(gameObject);
}
}
private void OnTriggerEnter2D(Collider2D other)
{
}
Try having an abstract base class that is common for both Enemy and Player- E.g Character or whatever makes sense for your program.
public abstract class Character
{
public virtual void OnTriggerEnter2D(Collider2D other)
{
DamageDealer damageDealer = other.gameObject.GetComponent<DamageDealer>();
ProcessHit(damageDealer);
};
public void ProcessHit(DamageDealer damageDealer)
{
//ProcessHit accessible for all classes that inherit Character
health -= damageDealer.GetDamage();
if (health <= 0)
{
Destroy(gameObject);
}
}
}
public class Enemy : Character
{
//this has both methods as declared in Character
}
public class Player : Character
{
// this needs to be overriden for Player
public override void OnTriggerEnter2D(Collider2D other)
{
//Enter OnTriggerEnter2D logic for Player
}
}
So if you want any class that has OnTriggerEnter2D to act in a different way, just override it and change the logic.
Related
For example, I have a variable "Wisps" that I want to change when the player picks up an object. But I don't know how to do it. I tried to add a WispDisplay object to call the classes, like in Java, but it doesn't seem to work.
public class WispCode : MonoBehaviour
{
WispDisplay wd = new WispDisplay();
private void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Player")
{
wd.setWisp(wd.getWisp()+1);
Destroy(gameObject);
}
}
}
public class WispDisplay : MonoBehaviour
{
public int Wisp = 5;
public Text WispText;
void Start()
{
}
void Update()
{
WispText.text = "Wisp: " + Wisp.ToString();
}
public int getWisp()
{
return Wisp;
}
public void setWisp(int newWisp)
{
Wisp = newWisp;
}
}
Easiest (a tiny bit dirty) way is to use a static variable. Downside: you can only have exactly ONE.
Example:
public class MyClass: MonoBehaviour {
public static int wisps;
}
Then, in ANY class, just use this to access it:
MyClass.wisps = 1234;
The more elegant way, working with multiple class instances, is using references.
Example:
public class PlayerClass: MonoBehaviour {
public int wisps = 0;
}
public class MyClass: MonoBehaviour {
public PlayerClass player;
void Update(){
player.wisps += 1;
}
}
Then, you need to drag-drop (aka "assign") the "PlayerClass" Component (attached to the player) to the the Gameobject that should increase the Wisps count. You can duplicate these objects after assigning the reference.
Now, if you actually want to have some sort of collectible, I'd suggest this approach:
You Have a Player "PlayerClass" and some Objects that are collectible, which have Trigger Colliders.
The objects have this code:
public class Example : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
// probably a good idea to check for player tag:
// other.compareTag("Player");
// but you need to create the "Player" Tag and assign it to Player Collider Object.
if(TryGetComponent(out PlayerClass player))
{
player.wisps += 1;
}
}
}
How do I get the number of coins collected in CoinPicker script and access it in LevelLoader script?
Here is my CoinPicker script:
public class CoinPicker : MonoBehaviour
{
public float coin = 0;
public TextMeshProUGUI textCoins;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.transform.tag == "Coins")
{
coin++;
textCoins.text = coin.ToString();
Destroy(other.gameObject);
}
}
}
Here is my LevelLoader script:
public class LevelLoader : MonoBehaviour
{
public int iLevelToLoad;
public string sLevelToLoad;
public bool useIntegerToLoadLevel;
private void OnTriggerEnter2D(Collider2D collision)
{
GameObject collisionGameObject = collision.gameObject;
if(collisionGameObject.gameObject.tag == "Player" )
{
LoadScene();
}
}
void LoadScene()
{
if(useIntegerToLoadLevel)
{
SceneManager.LoadScene(iLevelToLoad);
}
else
{
SceneManager.LoadScene(sLevelToLoad);
}
}
}
Please help, I am weak at this :(
You can make a get direct from Coin picker and get from level loader access the coin from CoinPicker.
public class LevelLoader : MonoBehaviour
{
public CoinPicker CoinPicker;
public int iLevelToLoad;
public string sLevelToLoad;
public bool useIntegerToLoadLevel;
private void Start()
{
Debug.log(CoinPicker.coin); //you can get coin from here
}
private void OnTriggerEnter2D(Collider2D collision)
{
GameObject collisionGameObject = collision.gameObject;
if(collisionGameObject.gameObject.tag == "Player" )
{
LoadScene();
}
}
void LoadScene()
{
if(useIntegerToLoadLevel)
{
SceneManager.LoadScene(iLevelToLoad);
}
else
{
SceneManager.LoadScene(sLevelToLoad);
}
}
}
You could use the same TextMesh you used in the Collision script. You would just put
public TextMeshProUGUI textCoins;
in the field of the second one, so that textCoins.text is the number of coins you collected. Then drag the same TextMesh into the TextMesh box in the Unity editor.
I can't tell what you are using the coins for in the Level loader, but if you wanted to check the number of coins that you collected, you would just say
if(int.Parse(textCoins.text) == RequiredNumberOfCoins)
I hope this makes sense.
So I have this script that affects another script just fine. It's attached to a gameobject (an attack box) that damages another gameobject (an enemy). It makes the enemy GameObject perform an animation (it getting hurt) and takes away a certain amount of health. That's all working fine.
What I'm stuck on is that I'm trying to get it to do the same for more than one type of enemy, therefore, accessing multiple scripts. The scripts are relatively the same and i've tested those out individually and both work fine. But when I try to have my attack box the script is attached to, affect more than one script, I get nothing. I figure it's just the way it's typed out and I've tried several ways already. But I've reverted it back to its most simple form to display it here. How do I get this script to work for both, so I don't have to have multiple scripts attached to one hitbox?
I should mention that in this script, it does access the first script mentioned in the OnTriggerEnter2D function. It just doesn't do it for any other scripts mentioned afterwards.
using UnityEngine;
using System.Collections;
public class slicer : MonoBehaviour {
public int damage = 5;
private foeHP foe;
private goblin gobby;
public float timer;
void Update()
{
Destroy ();
timer -= Time.deltaTime;
}
public void OnTriggerEnter2D (Collider2D other)
{
if (other.gameObject.tag == "Enemy") {
other.gameObject.GetComponent<foeHP> ().takeDamage (damage);
var foe = other.GetComponent<foeHP> ();
other.gameObject.GetComponent<goblin> ().takeDamage (damage);
var gobby = other.GetComponent<goblin> ();
}
if (foe == null) {
return;
}
if (gobby == null) {
return;
}
}
public void Destroy(){
if (timer <=0)
Destroy(gameObject);
}
}
Declare a generic Enemy class that all enemy types derive from.
public class Enemy : MonoBehaviour
{
int health;
public void TakeDamage(int amount)
{
health -= amount;
}
}
Change your enemy classes such that they all derive from Enemy
public class Goblin : Enemy
{
// Extra fields/methods
}
public class Foe : Enemy
{
// Extra fields/methods
}
Now you can simplify your checks into:
public void OnTriggerEnter2D (Collider2D other)
{
if (other.gameObject.tag == "Enemy")
{
other.GetComponent<Enemy>().TakeDamage(5);
}
}
Since both Goblin and Foe are type Enemy, GetComponent<Enemy>() will return their respective derived type and you can call TakeDamage() on them.
I am facing a problem in Unity3D. I have the same health script attached to both the player and the enemy. I want to show the game-over message when the player dies but the issue is that the game over message appears for both the player and enemy when they die.
My code looks like is that:
public class CharacterStats : MonoBehaviour
{
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
health = Mathf.Clamp (health, 0, 100);
}
public void damage(float damage)
{
health -= damage;
if(health<=0)
{
Die();
Application.LoadLevel(gameover);
}
}
void Die()
{
characterController.enabled = false;
if (scriptstodisable.Length == 0)
return;
foreach (MonoBehaviour scripts in scriptstodisable)
scripts.enabled = false;
if (ragdollmanger != null)
ragdollmanger.Raggdoll();
}
}
As you are using 1 script for both player and enemy. You should have different classes for both and implement an interface or derive from a base class to implement health:
public class Character : MonoBehaviour
{
public float Health;
public virtual void Damage(float damageValue)
{
Health -= damageValue;
}
public virtual void Die()
{
}
}
public Enemy : Character
{
public override void Die()
{
// do enemy related stuff
}
}
public Player : Character
{
public override void Die()
{
// do player related stuff.
// like game over screen
}
}
Hope this helps :)
You could use a bool to check whether CharacterStats is attached to the player, for example by adding a tag called ”Player” to the player game object and checking if gameObject.tag == “Player”, or you could equivalently name the game object ”Player” and check gameObject.name if you so wish.
You could then run the function for the game over message only if the game object is a player (isPlayer is true).
public class CharacterStats : MonoBehaviour
{
bool isPlayer = false;
// Use this for initialization
void Start ()
{
if(gameObject.tag == “Player”)
{
isPlayer = true;
}
}
// Update is called once per frame
void Update ()
{
health = Mathf.Clamp (health, 0, 100);
}
public void damage(float damage)
{
health -= damage;
if(health<=0)
{
if(isPlayer)
{
// Do Player-only stuff
}
// Do Stuff for both players and enemies
}
}
}
In Unity3D my enemy is not taking damage upon colliding with my projectile explosion.
Although this is not the case as it the health variable is unaffected upon colliding with my projectile explosion.
My Enemy and Barrel classes inherit from Entity which handles the taking of damage (subtracting the damage variable from the health variable). Although only the barrel class is working as intended.
The tags are 100% correct and I would prefer to continue using inheritance so please no suggestions to change the method in which my classes take damage.
the class that Enemy and Barrel inherit from
using UnityEngine;
using System.Collections;
public class Entity : MonoBehaviour {
public float health = 25;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public virtual void takeDamage(float dmg){
health -= dmg;
if (health <= 0){
Destroy(this.gameObject);
}
}
}
Enemy class
using UnityEngine;
using System.Collections;
public class Enemy : Entity {
private NavMeshAgent agent;
public GameObject target;
// Use this for initialization
void Start () {
agent = GetComponent<NavMeshAgent> ();
}
// Update is called once per frame
void Update () {
agent.SetDestination (target.transform.position);
}
}
Barrel class
using UnityEngine;
using System.Collections;
public class Barrel : Entity {
private Transform myTransform;
//Effects
public GameObject barrelExplosion;
public GameObject explosionDamage;
public GameObject explosionSound;
// Use this for initialization
void Start () {
myTransform = this.transform;
}
// Update is called once per frame
void Update () {
}
public override void takeDamage(float dmg){
health -= dmg;
if (health <= 0){
Instantiate(barrelExplosion, myTransform.position, myTransform.rotation);
Instantiate(explosionSound, myTransform.position, myTransform.rotation);
Instantiate(explosionDamage, myTransform.position, myTransform.rotation);
Destroy(this.gameObject);
}
}
}
ExplosionAOE the class that sends the damage
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ExplosionAOE : MonoBehaviour {
public float damage = 100.0f;
public float lifeTime = 0.05f;
private float lifeTimeDuration;
public List<GameObject> damageTargets = new List<GameObject>();
public float radius = 15.0f;
GameManager gameManager;
void Start() {
gameManager = GameObject.FindGameObjectWithTag("GameManager").GetComponent<GameManager>();
//Destroy (this.gameObject, lifeTime);
lifeTimeDuration = Time.time + lifeTime;
transform.GetComponent<SphereCollider>().radius = radius;
}
void Update() {
//Explosion finishes, damage targets and remove AOE field
if (Time.time > lifeTimeDuration) {
foreach (GameObject target in damageTargets) {
if (target != null) {
//Calculate damage based on proximity to centre of explosion
float thisDamage = ((radius - Vector3.Distance(target.transform.position, transform.position)) / radius) * damage;
print(thisDamage);
target.GetComponent<Entity>().takeDamage(thisDamage);
//target.SendMessage("takeDamage", damage); //<< This is not good code. Let's fix this!
}
}
Destroy(this.gameObject);
}
}
void OnTriggerEnter(Collider otherObject) {
if (otherObject.gameObject.tag == "Enemy") {
damageTargets.Add(otherObject.gameObject);
}
if (otherObject.gameObject.tag == "Player") {
Vector3 jumpVector = (otherObject.transform.position - transform.position).normalized;
jumpVector *= 25;
otherObject.GetComponent<CharacterMotor>().SetVelocity(jumpVector);
}
}
}
Sorry this is a bit of a lengthy one and EVERYTHING is tagged correctly so that is not the issue, thanks.
Problem 1.
Use "Debug.Log" everywhere
void OnTriggerEnter(Collider otherObject) {
Debug.Log("in trig");
Debug.Log("otherObject.gameObject.tag is " + otherObject.gameObject.tag);
if (otherObject.gameObject.tag == "Enemy") {
Debug.Log("a");
damageTargets.Add(otherObject.gameObject);
}
if (otherObject.gameObject.tag == "Player") {
Debug.Log("b");
Vector3 jumpVector = (otherObject.transform.position -
transform.position).normalized;
jumpVector *= 25;
otherObject.GetComponent<CharacterMotor>().SetVelocity(jumpVector);
}
}
In particular, in Entity and Enemy.
Questions such as this one are instantly answered by tracking with Debug.Log.
Problem 2.
It's a PITA getting the relationships between triggers, rigidbody, etc.
It's very likely that's a problem here.
http://docs.unity3d.com/Manual/CollidersOverview.html
Go down to the annoying "trigger action matrix" and work from there.
Problem 3.
As a rule, never use the "tags" feature in Unity. (They only added tags to help "hello world" tutorials.)
In practice you use layers everywhere and always:
(Layers are particularly essential in shooting games: every single category needs a layer.)
Problem 4.
The code shown is definitely looking good. Here's some example code not unlike yours for tips.
Trivial example, note the breakaway code (the returns) inside the OnTrigger, you should do that).
Also,
use extentions
everywhere and always in Unity. Quick tutorial
it's the #1 tip if you actually want to work professionally.
public class Enemy:BaseFrite
{
public tk2dSpriteAnimator animMain;
public string usualAnimName;
[System.NonSerialized] public Enemies boss;
[Header("For this particular enemy class...")]
public float typeSpeedFactor;
public int typeStrength;
public int value;
// could be changed at any time during existence of an item!
[System.NonSerialized] public FourLimits offscreen; // must be set by our boss
[System.NonSerialized] public int hitCount; // that's ATOMIC through all integers
[System.NonSerialized] public int strength; // just as atomic!
[System.NonSerialized] public float beginsOnRight;
private bool inPlay; // ie, not still in runup
void Awake()
{
boss = Gp.enemies;
}
..........
protected virtual void Prepare() // write it for this type of sprite
{
ChangeClipTo(bn);
// so, for the most basic enemy, you just do that.
// for other enemy, that will be custom (example, swap damage sprites, etc)
}
void OnTriggerEnter2D(Collider2D c)
{
// we can ONLY touch either Biff or a projectile. to wit: layerBiff, layerPeeps
GameObject cgo = c.gameObject;
if ( gameObject.layer != Grid.layerEnemies ) // if we are not enemy layer....
{
Debug.Log("SOME BIZARRE PROBLEM!!!");
return;
}
if (cgo.layer == Grid.layerBiff) // we ran in to Biff
{
Gp.billy.BiffBashed();
// if I am an enemy, I DO NOT get hurt by biff smashing in to me.
return;
}
if (cgo.layer == Grid.layerPeeps) // we ran in to a Peep
{
Projectile p = c.GetComponent<Projectile>();
if (p == null)
{
Debug.Log("WOE!!! " +cgo.name);
return;
}
int damageNow = p.damage;
Hit(damageNow);
return;
}
Debug.Log("Weirded");
}
public void _stepHit()
{
if ( transform.position.x > beginsOnRight ) return;
++hitCount;
--strength;
ChangeAnimationsBasedOnHitCountIncrease();
// derived classes write that one.
if (strength==0) // enemy done for!
{
Gp.coins.CreateCoinBunch(value, transform.position);
FinalEffect();
if ( Gp.superTest.on )
{
Gp.superTest.EnemyGottedInSuperTest(gameObject);
boss.Done(this);
return;
}
Grid.pops.GotEnemy(Gp.run.RunDistance); // basically re meters/achvmts
EnemyDestroyedTypeSpecificStatsEtc(); // basically re achvments
Gp.run.runLevel.EnemyGotted(); // basically run/level stats
boss.Done(this); // basically removes it
}
}
protected virtual void EnemyDestroyedTypeSpecificStatsEtc()
{
// you would use this in derives, to mark/etc class specifics
// most typically to alert achievements system if the enemy type needs to.
}
private void _bashSound()
{
if (Gp.biff.ExplodishWeapon)
Grid.sfx.Play("Hit_Enemy_Explosive_A", "Hit_Enemy_Explosive_B");
else
Grid.sfx.Play("Hit_Enemy_Non_Explosive_A", "Hit_Enemy_Non_Explosive_B");
}
public void Hit(int n) // note that hitCount is atomic - hence strength, too
{
for (int i=1; i<=n; ++i) _stepHit();
if (strength > 0) // biff hit the enemy, but enemy is still going.
_bashSound();
}
protected virtual void ChangeAnimationsBasedOnHitCountIncrease()
{
// you may prefer to look at either "strength" or "hitCount"
}
protected virtual void FinalEffect()
{
// so, for most derived it is this standard explosion...
Gp.explosions.MakeExplosion("explosionC", transform.position);
}
public void Update()
{
if (!holdMovement) Movement();
if (offscreen.Outside(transform))
{
if (inPlay)
{
boss.Done(this);
return;
}
}
else
{
inPlay = true;
}
}
protected virtual void Movement()
{
transform.Translate( -Time.deltaTime * mpsNow * typeSpeedFactor, 0f, 0f, Space.Self );
}
......
/*
(frite - flying sprite)
The very base for enemies, projectiles etc.
*/
using UnityEngine;
using System.Collections;
public class BaseFrite:MonoBehaviour
{
[System.NonSerialized] public float mpsNow;
// must be set by the boss (of the derive) at creation of the derive instance!
private bool _paused;
public bool Paused
{
set {
if (_paused == value) return;
_paused = value;
holdMovement = _paused==true;
if (_paused) OnGamePause();
else OnGameUnpause();
}
get { return _paused; }
}
protected bool holdMovement;
protected virtual void OnGamePause()
{
}
protected virtual void OnGameUnpause()
{
}
protected string bn;
public void SetClipName(string clipBaseName)
{
bn = clipBaseName;
}
}
Is more easy if in ExplosionAOE/OnTriggerEnter function you call the takeDamage function:
scriptCall = otherObject.GetComponent(EnemyScript);
scriptCall.takeDamage(damage);