Lesson 8: Attack Methods

No class for Week 8, but I have added some videos for a recent lesson developed for another class that feature a simplified character controller, as well as methods for attacking with stomp, melee, and projectile attacks.

Perhaps the most useful component of this is the OnDrawGizmos( ) and OnDrawGizmosSelected( ) method which can be used to draw info in your Editor’s Scene view. These are very useful for visualizing things like areas of effect, especially for those things that use triggers or physics calls. This method is called automatically by the engine just like “Update()” but are even running while the game is not playing, as this is an editor specific tool.

Check out the Unity Script Reference – Gizmos for more…

There has been a request for the player movement and alternate character controller script. These have been posted below.


SideScrollerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
[RequireComponent(typeof(CapsuleCollider2D))]

public class SideScrollerController : MonoBehaviour
{
    // public instance variables
    public float maxSpeed = 1f;
    public float jumpHeight = 1f;
    public float gravityScale = 1f;

    // direction tracking
    private bool facingLeft = false;

    // movement states
    public bool stunned = false;
    public bool isAttacking = false;

    // ground check values
    [Header("Ground Check Values")]
    public float groundCheckRadius = 0f;
    public float groundCheckYOffset = 0f;
    public LayerMask layerMask;

    [Header("Melee Attack Values")]
    public float attackXOffset = 0f;
    public float attackYOffset = 0f;
    public float attackRadius = 0.1f;
    public LayerMask attackLayerMask;

    [Header("Fireball Values")]
    public GameObject fireballPrefab;
    public float fireballSpeed;

    // private instance variables
    float moveDirection = 0;
    public bool isGrounded = false;

    Rigidbody2D r2d;
    CapsuleCollider2D mainCollider;
    Animator animator;
    SpriteRenderer spriteRenderer;

    // Start is called before the first frame update
    void Start()
    {
        r2d = GetComponent<Rigidbody2D>();
        mainCollider = GetComponent<CapsuleCollider2D>();
        animator = GetComponent<Animator>();
        spriteRenderer = GetComponent<SpriteRenderer>();

        // set some properties
        r2d.freezeRotation = true;
        r2d.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
        r2d.gravityScale = gravityScale;

    }

    // Update is called once per frame
    void Update()
    {
        // Are we stunned?

        if (!stunned)
        {
            // we can move
            // are we moving horizontally?  Input.GetAxisRaw(H)
            moveDirection = Input.GetAxisRaw("Horizontal");

            // left/right facing logic here
            if ((moveDirection > 0f) &amp;&amp; (facingLeft))
            {
                facingLeft = false;
            }
            else if ((moveDirection < 0f) &amp;&amp; (!facingLeft))
            {
                facingLeft = true;
            }

            if (Input.GetButtonDown("Jump") &amp;&amp; isGrounded)
            {
                r2d.velocity = new Vector2(r2d.velocity.x, jumpHeight);
            }

            if (Input.GetButtonDown("Fire1") &amp;&amp; isGrounded)
            {
                // sword attack
                animator.SetTrigger("SwordAttack");

                // call the melee function
                // MeleeAttack();
            }

            if (Input.GetButtonDown("Fire2"))
            {
                // shoot a fireball
                ShootFireball();
            }

        }

        // update the animator

        animator.SetBool("Grounded", isGrounded); // tell the animator is this grounded
        animator.SetFloat("VerticalSpeed", r2d.velocity.y); // get the downward velocity
        animator.SetFloat("Speed", Mathf.Abs(moveDirection)); // get the horizontal speed
        


        // update the direction
        spriteRenderer.flipX = facingLeft;

    }

    private void FixedUpdate()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(GetGroundCheckPosition(), groundCheckRadius, layerMask);

        // Check if any of the overlapping colliders are not player collider, if so, set isGrounded to true
        isGrounded = false;
        
        if (colliders.Length > 0)
        {
            for (int i = 0; i < colliders.Length; i++)
            {
                if (colliders[i] != mainCollider)
                {
                    isGrounded = true;
                    break;
                }
            }
        }


        if (!stunned) { 
            // apply the movement velocity
            r2d.velocity = new Vector2((moveDirection) * maxSpeed, r2d.velocity.y);
        }
    }

    private Vector3 GetGroundCheckPosition()
    {
        Vector3 groundCheckPos = transform.position + new Vector3(0, groundCheckYOffset, 0);
        return groundCheckPos;
    }

    private Vector3 GetAttackPosition()
    {
        float direction = 1f;
        if (facingLeft)
        {
            direction = -1f;
        }

        Vector3 meleeAttackZone = transform.position + new Vector3(attackXOffset * direction, attackYOffset, 0);
        return meleeAttackZone;
    }

    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.cyan;
        Gizmos.DrawWireSphere(GetGroundCheckPosition(), groundCheckRadius);

        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(GetAttackPosition(), attackRadius);

    }

    private void MeleeAttack()
    {
        // what is in our range?
        Collider2D[] colliders = Physics2D.OverlapCircleAll(GetAttackPosition(), attackRadius, attackLayerMask);

        // an array of colliders
        foreach (Collider2D thisCollider in colliders )
        {
            /*
            Debug.Log(thisCollider);
            thisCollider.SendMessage("SuccessfulHit");
            */

            EnemyScript enemy = thisCollider.gameObject.GetComponent<EnemyScript>();
            if (enemy)
            {
                enemy.SuccessfulHit();
            }
        }

    }

    private void ShootFireball()
    {
        // instantiate the fireball
        GameObject fireball = Instantiate(fireballPrefab, transform.position, Quaternion.identity);
        Vector3 fireballVelocity = Vector3.right * fireballSpeed;

        if (facingLeft)
        {
            // flip the object
            Vector3 fireballScale = fireball.transform.localScale;
            fireballScale.x *= -1.0f;
            fireball.transform.localScale = fireballScale; // put the new vector back

            // reverse the velocity
            fireballVelocity.x *= -1.0f;
        }

        fireball.GetComponent<Rigidbody2D>().velocity = fireballVelocity;

    }

    public void StunIsOver()
    {
        // remove the stun from the character
        stunned = false;
    }

}


PlayerScript.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerScript : MonoBehaviour
{
    Animator animator;
    Rigidbody2D rb;
    SideScrollerController controller;



    public float pushbackForce;

    // Start is called before the first frame update
    void Start()
    {
        animator = GetComponent<Animator>();
        rb = GetComponent<Rigidbody2D>();
        controller = GetComponent<SideScrollerController>();
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            EnemyScript enemy = collision.gameObject.GetComponent<EnemyScript>();
            

            if (controller.stunned == false &amp;&amp; enemy.isAlive)
            {

                controller.stunned = true;

                // we have been hurt
                animator.SetTrigger("IsHurt");

                // pushback when hurt
                Vector3 pushback = collision.transform.position - transform.position;
                Debug.Log("Pushback = " + pushback);

                Vector3 currentVelocity = rb.velocity;

                currentVelocity.x = pushback.x * -pushbackForce;

                rb.velocity = currentVelocity; // change the direction of our object

            }

        }
    }




}