Unity 2D Melee Combat System - c#

i am a beginner game devepoler and i want to make a 2D action platformer game. I am trying to make a fluent combat system. I have researched this a lot but everywhere i found the same piece of code but it seems buggy and not reliable to me; for example it's damaging always twice, sometimes it doesnt respond to the key, when you held down the key its spamming etc. So i need a cleaner or better code, can you help me?
Weapon Script:
private float timeBtwAttack;
public float startTimeBtwAttack;
public Transform attackPos;
public float attackRange;
public LayerMask whatIsEnemies;
public int damage = 40;
int steal = 20;
public PlayerHealth playerHealth;
void FixedUpdate()
{
if(timeBtwAttack <= 0){
if(Input.GetKey(KeyCode.F)){
Collider2D [] enemiesToDamage = Physics2D.OverlapCircleAll(attackPos.position, attackRange, whatIsEnemies);
for (int i = 0; i < enemiesToDamage.Length; i++)
{
enemiesToDamage[i].GetComponent<EnemyHealth>().TakeDamage(damage);
}
playerHealth.LifeSteal(steal);
}
timeBtwAttack = startTimeBtwAttack;
}else{
timeBtwAttack -= Time.deltaTime;
}
}
Enemy Health Script:
public int maxHealth = 100;
int currentHealth;
void Start()
{
currentHealth = maxHealth;
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
Debug.Log("Damage Taken");
if(currentHealth <= 0)
{
Die();
}
}
void Die(){
Debug.Log("Enemy Died");
}
I have tried lots of different tutorials but everytime it was almost always the same piece of code.

Related

Player now does not jump after adding health in Unity

so first time learning C# for my team studio class. I found this video which I was able to get my player moving and jumping perfectly, https://www.youtube.com/watch?v=3GtQ2yQX2kU&t=569s however after I added a health/can die system to the script, now the player refuses to jump no matter what I do. Since I am a newbie at this, I am just going to post the whole thing here if anyone can point me in the right direction. I've played with the box colliders, added the layers to the platforms like the tutorial said... please, if anyone can help. I know I'm a newbie to this :(
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public HealthBar healthBar; //Communicates with the healthbar script
public float moveSpeed;
public float jumpForce;
public int jumpsAmount;
int jumpsLeft;
public Transform GroundCheck;
public LayerMask GroundLayer;
public int maxHealth = 3;
public int currentHealth;
bool isGrounded;
float moveInput;
Rigidbody2D rb2d;
float scaleX;
// Start is called before the first frame update
void Start()
{
healthBar.SetMaxHealth(maxHealth); //Sets healthbar to max health
currentHealth = maxHealth;
rb2d = GetComponent<Rigidbody2D>();
scaleX = transform.localScale.x;
}
public void TakeDamage(int damage)
{
currentHealth -= damage;
if (currentHealth <= 0) //When Health is zero the player is destroyed
{
Destroy(gameObject);
}
healthBar.SetHealth(currentHealth); //Updates Healthbar when damgage is taken
}
// Update is called once per frame
void Update()
{
moveInput = Input.GetAxisRaw("Horizontal");
Jump();
}
private void FixedUpdate()
{
Move();
}
public void Move()
{
Flip();
rb2d.velocity = new Vector2(moveInput * moveSpeed, rb2d.velocity.y);
}
public void Flip()
{
if (moveInput > 0)
{
transform.localScale = new Vector3(scaleX, transform.localScale.y, transform.localScale.z);
}
if (moveInput < 0)
{
transform.localScale = new Vector3((-1) * scaleX, transform.localScale.y, transform.localScale.z);
}
}
public void Jump()
{
if (Input.GetKeyDown(KeyCode.Space))
{
CheckIfGrounded();
if (jumpsLeft > 0)
{
rb2d.velocity = new Vector2(rb2d.velocity.x, jumpForce);
jumpsLeft--;
}
}
}
public void CheckIfGrounded()
{
isGrounded = Physics2D.OverlapCircle(GroundCheck.position, GroundCheck.GetComponent<CircleCollider2D>().radius, GroundLayer);
ResetJumps();
}
public void ResetJumps()
{
if (isGrounded)
{
jumpsLeft = jumpsAmount;// jumpsAmount =2;
}
}
}
I put the groundcheck collider where the video wanted me to, even played around having different locations too. Nothing is working... tried changing the jump force too to see if it needs to be higher. I am at a complete loss.

Formula to deal damage - armor

I'm just starting to learn c# and got stuck in this exercise.
I need to create a formula to deal damage - armor. The armor is reduced with each hit. If I write
float damage = hp+armor-(physicalDamage-damageStrength)it doesn't damage the enemy. The task says I should change only `float damage = ...' for this code to work. So, the mistake can be only in this line float damage =
[RequireComponent(typeof(Animator))]
public class Goblin : MonoBehaviour, IDamagable {
static private string hitTriggerName = "Hit";
[SerializeField] private float hp = 800; // health
[SerializeField] private float armor = 100; // armor
[SerializeField] private float armorStrength = 5; // armor decrease each time
[SerializeField] private int n = 0; // number of hits
private Animator selfAnimator;
private void Awake() {
selfAnimator = GetComponent<Animator>();
}
public void ApplyDamage(float physicalDamage, float damageStrength) {
float damage = hp+armor-physicalDamage-damageStrength;
if (damage < 0) {
hp += damage;
}
n += 1;
armor -= armorStrength;
selfAnimator.SetTrigger(hitTriggerName);
}
public void onHitAnimationEnd() {
if (hp <= 0) {
Kill();
}
}
private void Kill() {
Destroy(gameObject);
}
}```
***Corersponding part of a "player" object***
```using System;
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class Player : MonoBehaviour {
static private string attackTriggerName = "Attack";
[SerializeField] private float attackCooldown = 0.1f;
[SerializeField] private Goblin aim;
private float timeToNextAttack = 0f;
private Animator selfAnimator;
private void Awake() {
selfAnimator = GetComponent<Animator>();
}
[Serializable] private class Staff {
[SerializeField] private float physicalDamage = 100; // staff damage
[SerializeField] private float damageStrength = 5; // damage reduction each time
public void Use(IDamagable aim) {
if (aim != null) {
aim.ApplyDamage(physicalDamage, damageStrength);
}
}
}
[SerializeField] private Staff weapon;
private void Attack() {
selfAnimator.SetTrigger(attackTriggerName);
weapon.Use(aim);
timeToNextAttack = attackCooldown;
}
private void Update() {
if (Input.GetKeyDown(KeyCode.Space) && timeToNextAttack <= 0) {
Attack();
}
timeToNextAttack -= Time.deltaTime;
}
}```
I may be wrong or didn't understand, but by quickly looking at the code, I'd suggest you declare the damage variable above the Awake function. And give it a 0.0f value by default.
I think you made two unrelated mistakes:
damage sould be only: float damage = armor-physicalDamage
damageStrength should not be a parameter of the method because it should be applied to the staff physicalDamage after the hit, like you did to the armor.

Score board moves with Character movement in Unity C#

I have a C# script which is for a game in unity. When I run the scene, the Player/ character moves with the score board (want the score board not to move, for obvious reasons). I had a issue with Nullref error before, but fixed that and now the score board is an issue. All objects were imported/ created in the Assets folder and dragged to the world scene/ main scene.
Here's the code:
public class Script : MonoBehaviour
{
public AudioClip collectCoins;
public AudioSource audioSource;
public Text _mytext;
int i = 0;
private void Start()
{
}
public void OnCollisionEnter(Collision collision)
{
if (collision.collider.CompareTag("coin"))
{
i += 10;
audioSource.PlayOneShot(collectCoins);
Destroy(collision.gameObject);
}
if (collision.collider.CompareTag("coin2"))
{
i -= 10;
if (i < 0)
{
i = 0;
}
audioSource.PlayOneShot(collectCoins);
Destroy(collision.gameObject);
}
}
private void FixedUpdate()
{
}
float speed =0.2f;
void Update()
{
if(_mytext != null)
{
_mytext.text = "Bonus: " + i;
}
float move = Input.GetAxis("Horizontal") * speed;
transform.Translate(move, 0, 0);
}
}
Its the strangest thing...Tried everything...any ideas to what I should do? (appreciate any replies).

Script connection to other Script

I have a script in diferents objects to my boss. I program the script when the healt of boss is 10 or lower the speed to the other script is set to 4. I attach the first script to other script but dosen't work, the speed isn't change when the healt is low and i have no idea whats is the problem.
public int health = 12;
private gameMaster gm;
private UnityEngine.Object explosionRef;
public GameObject Hiedra;
public bool isHurt = false;
private HiedraScript ChangeVelocity;
void Start()
{
gm = GameObject.FindGameObjectWithTag("GameMaster").GetComponent<gameMaster>();
explosionRef = Resources.Load("Explosion");
ChangeVelocity = gameObject.GetComponent<HiedraScript>();
}
void Update()
{
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Bullet"))
{
Destroy(collision.gameObject);
health--;
Hiedra.GetComponent<Animation>().Play("Player_RedFlash");
if (health <= 10)
MovesFaster();
isHurt = true;
if (health <= 0)
KillSelf();
}
if (health <= 0)
{
gm.points += 20;
}
}
private void MovesFaster()
{
ChangeVelocity.moveSpeed = 4f;
}
and the other value in diferent script:
public float moveSpeed = 1f;
Did you try to Debug.Log()?
If unity logging something so you have idea if it really calls MovesFaster() Function.
If so, you can mark moveSpeed variable as static and you can access it. I think this variable in a class inherited from monobehaviour. If you want to all public variable even that not static you can use singleton pattern. Singleton pattern is so useful in many cases.

Cause player to take damage in Unity 2D

I made two scripts. One that'll keep track of the player's health, health bar and cause the screen to flash when the player is damaged. The other script is meant to be placed on any object I wish to do damage to the player, on contact. My problem is, Nothing seems to be doing any damage to the player.
PlayerHealth.cs:
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
public int currentHealth;
public float flashSpeed = 5;
public Slider healthSlider;
public Color flashColour = new Color(1, 0, 0, 0.1f);
bool isDead;
bool damaged;
private void Awake()
{
currentHealth = 100;
}
private void Update()
{
damaged = false;
}
public void TakeDamage(int amount)
{
damaged = true;
currentHealth -= amount;
healthSlider.value = currentHealth;
}
}
AttackPlayer.cs:
using UnityEngine;
public class AttackPlayer : MonoBehaviour
{
public float timeBetweenAttacks = 0.5f;
public int attackDamage = 10;
GameObject player;
PlayerHealth playerHealth;
float timer;
private void Awake()
{
player = GameObject.FindGameObjectWithTag("Player");
playerHealth = player.GetComponent<PlayerHealth>();
}
private void OnTriggerEnter2D(Collider2D col)
{
if (col.gameObject == player)
{
Attack();
}
}
private void Update()
{
timer += Time.deltaTime;
if(playerHealth.currentHealth <=0)
{
// TODO: add death script here.
}
}
void Attack()
{
timer = 0f;
if(playerHealth.currentHealth > 0)
{
playerHealth.TakeDamage(attackDamage);
}
}
}
The player has a rigidbody2D. The player and damaging objects have Box Collider 2D's on them.
Make sure that the players Collider has isTrigger enabled.
attackDamage is public -> set in the inspector. Make sure it is not 0.
You could use
[Range(1,100)] public int attackDamage = 10;
to automatically clamp the value in the inspector.
A guess but I'ld say your Collider might not be on the GameObject player but probably on one of its children => the condition col.gameObject == player is not true.
Instead of GameObject references rather compare the PlayerHealth (since there is only one) reference like
private void OnTriggerEnter2D(Collider2D col)
{
// gets PlayerHealth component on this or any parent object
var health = col.GetComponentInParent<PlayerHealth>();
if (health == playerHealth)
{
Attack();
}
}
You have
private void Update()
{
damaged = false;
}
public void TakeDamage(int amount)
{
damaged = true;
currentHealth -= amount;
healthSlider.value = currentHealth;
}
I don't know what else should happen on TakeDamage but the value of damaged is resetted in Update so right after it was set by the Trigger because Physics events like OnTriggerEnter are executed before Update (see execution Order).
Hint: Instead of
player = GameObject.FindGameObjectWithTag("Player");
playerHealth = player.GetComponent<PlayerHealth>();
you could also use
playerHealth = FindObjectOfType<PlayerHealth>();
if that component exists only once in your scene.
Or to be more flexible (having multiple Players) all you have to do is change your OnTriggerEnter2D and Attack method to
private void OnTriggerEnter2D(Collider2D col)
{
// gets PlayerHealth component on this or any parent object
var health = col.GetComponentInParent<PlayerHealth>();
if (health != null)
{
Attack(health);
}
}
void Attack(PlayerHealth health)
{
timer = 0f;
if(health.currentHealth > 0)
{
health.TakeDamage(attackDamage);
}
}
So you wouldn't need to get the reference before.

Categories