Need Advice on my Scripting Practice

Ok, so I’ve been working on something new and in the past few weeks of learning and going through tutorials I’ve been overwhelmed by how much I’ve forgotten about since I was in school. What I’m asking from you vets is if my scripts seem to be in the direction I need to be going. I’ve been working on my character movement and attacking script one little bit at a time. The script is to move the character with user input, play sounds on attacking and animate if he is close enough to the victim npc. Can someone who is good at this stuff just give my code a quick look and tell me if I’m doing things well enough? All of the tutorials I look at show little scripts all being put together to make things happen in their games. I can’t figure out a way to do it on this one. If there are ways to simplify what I’m doing I want to find out now before I get really involved with this project so I won’t have to go back redo everything.

Here’s a video of what the player does using this script:

using UnityEngine;
using System.Collections;

public class CharacterMovementScript : MonoBehaviour {

   public float playerSpeed = 10f;     //sets player's speed

    private Vector3 moveDirection = Vector3.zero;   //initialized player movement direction values
    private Vector3 fallDirection = Vector3.zero;
    public float gravityValue = 1; //sets value for gravity for when the player is falling off edges 1 is slower, 5+ is very fast

    private bool canMove = true;          //used to disable movement while attacking.
    private bool attacking = false;        //for timer during attack

    private float WaitToAttack = 1.0f;     //0.917f;    //this MUST be set to length of attacking animation for smooth player input

    private float AttackTime = 0f;          //used for determining length of grabbing animation
    private float attackSpeedBoost = 1.5f;
    private bool eatVictim = false;         //used to determine code that enables eating actions
    private float eatingTime = 2.375f;       //match this with time it takes Player Zombie eating animation to finish
    private float distanceFromVictim = 5f;
    private float eatingAnimationTime = 2.2f;   //MUST match the time length of the eating people animation for smooth play

    Animator anim;  //animator object
    CharacterController controller;
    AudioSource audio;
    public AudioClip[] zombieTalk;  //initialize audio clips from inspector.
    private int clipNumbers;

    private int rayhits = 0;
    // Use this for initialization

    void Start () {

        clipNumbers = zombieTalk.Length - 2;    //reason for -2 is because the last 2 in the list are for eating people

        controller = GetComponent<CharacterController>();
        anim = GetComponent<Animator>();
        audio = GetComponent<AudioSource>();

        anim.SetBool("isWalking", false);
    }

    // Update is called once per frame

    void Update () {

        if(Input.GetKeyDown("escape")) Application.Quit(); // Quits the game when pressing Escape

        if (canMove) MovePlayer();       //calls function to move player in the direction of user input
      
        Attack();           //checks to see if player is attacking

    }

    void Attack()
    {
        if (Input.GetButtonUp("Fire1") && eatVictim == false && !attacking)
        {
            attacking = true;
            anim.SetBool("isAttacking", true);  //set isAttacking true so animation plays right
            PlayerAudio();
        }

       

        if (attacking)           //determine if currently attacking
        {
            //determines if still attacking within the timeframe of the animation (WaitToAttack)
            //WaitToAttack must be in sync with time it takes to animate the actual attack
            if (AttackTime < WaitToAttack)
            {
                AttackTime += Time.deltaTime;
            }
            if (AttackTime >= WaitToAttack && eatVictim == false)
            {
                anim.SetBool("isAttacking", false);
                print("Attack finished");
                canMove = true;
                attacking = false;
                AttackTime = 0f;
            }

            //check to see if InnocentHuman victim is within grab range. If so, begin eating
            Ray ray = new Ray(transform.position, transform.forward);
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit, 0.4f))    //set 3rd variable to distance player has to be from victim before grabbing
            {
                if (hit.collider.gameObject.tag == "InnocentHuman") //check if innocent human is in grasp
                {
                    canMove = false;    //disable movement during attack
                    eatVictim = true;   //enable eating of the victim
                    int eatingAudioClipNumber = Random.Range(0, 2);   //get random one of the 2 eating clips 

                    hit.collider.gameObject.GetComponent<BlondeGirlAI>().GettingEaten(); //call script on Blonde Girl to be eaten    
                    audio.Stop();   //stop audio currently playing
                    audio.PlayOneShot(zombieTalk[clipNumbers + eatingAudioClipNumber]); //play audio for eating victim
                    eatingTime = 0f;    //reset eatingTime to 0 for timer
                }             
            }

            if (eatVictim)
            {
                anim.SetBool("isEating", true);             
                eatingTime += Time.deltaTime;
            }

            //timer to determine if eating animation has finished before proceeding
            if (eatingTime >= eatingAnimationTime && eatVictim == true)
            {
                eatingTime = 0f;
                eatVictim = false;
                anim.SetBool("isEating", false);
            }
        }
    }

    void PlayerAudio()
    {
        if (!eatVictim)
        {
            //cycles through audio clip for the zombie attack
            int clipNumber = Random.Range(0, clipNumbers);
            audio.PlayOneShot(zombieTalk[clipNumber]);  //play currently selected audio clip for attacking
        }
    }

    void MovePlayer()
    {
        //grab movement direction depending on player input
        moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); //get direction of movement

        if (moveDirection != Vector3.zero)
            //(moveHorizontal > 0 || moveVertical > 0 || moveHorizontal < 0 || moveVertical < 0)
        {
            anim.SetBool("isWalking", true);
            transform.LookAt(transform.position + moveDirection);  //look towards movement direction

            //move player in the direction retrieved
            controller.Move(moveDirection * Time.deltaTime * playerSpeed);
        }
        else
        {
            anim.SetBool("isWalking", false);
        }

        //check if player is not on the ground. if player is not on the ground, make player fall
        if(!controller.isGrounded)
        {
            fallDirection = new Vector3(0, -(gravityValue), 0);  //change movement direction to fall down
            controller.Move(fallDirection * Time.deltaTime);    //make player fall to the ground
        }

    }

}

Your script looks alright in my opinion.

A few tweaks would make it neater but what matters is it does what it needs to do.

I’ve been re-reading Terry Norton’s Learning C# by Developing Games with Unity 3D. He takes a different approach, implementing a State Machine with an Interface and an implementing class for each state in the game. Update() and OnGUI() - yes, it’s a bit dated - simply delegate to methods of the interface on whichever state is active at any time. It’s a much more OO approach which I like a lot better. Well worth having a look at if you’re interested in structuring your code.

Thanks for the replies. It seems I’m heading towards where I need to be. :slight_smile:

Just wanted to pop in to say I love the audio. :slight_smile:

I’d like to give a feedback as well. Personally, I think you should move your attack logic into another script. It seems easier to have everything in one script for convenience, but making, lets say, AttackScript with all your attacking/eating logic will allow you to make your code readable and reusable. It would look something like:

public AttackScript attackBehaviour;

void Update () {
        if(Input.GetKeyDown("escape")) Application.Quit(); // Quits the game when pressing Escape
        if (canMove) MovePlayer();       //calls function to move player in the direction of user input
    
       if (attackBehaviour != null)
       {
              attackBehaviour.Attack();
       }
    }

You could then give your AttackScript to those running NPCs who were unfortunate to turn into zombies and make them attack other non-zombie NPCs. Think of the possibilities :slight_smile: Also, you could easily turn on/off attacking just by enabling/disabling your attack component, let’s say you have hunger meter; if you’re full you can’t attack other NPCs or something.

2 Likes