At War With Christmas

At War With Christmas is een 4 player co-op free for all first person shooter.

Info:

Gemaakt met: Unity
Codeertaal: C#
Mijn rol: Programmer
Tijdspan: 2 weken

Link naar het project

Team:

Developers:
Alex van Hattem
Kenam Özbakan
Matthijs van den Bovenkamp

Mijn bijdrage in At War With Christmas​

Player Controller

Ik heb Player Controller gemaakt zodat je de met behulp van unity new input system players kan besturen met niet alleen een toetsenbord maar ook met en controller.  Je kunt de players rond laten kijken met je muis of met de joystick, laten bewegen met de toetsen WASD of met de andere joystick. Je kan ook springen met spatiebalk of met de onderste knop op een controller, klimmen op ladders maar ook bukken met control of met meest rechtse knop op een controller.

C#
using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    public int ID;
    private InputActionAsset inputAsset;
    private InputActionMap player;
    private InputAction movement;
    private InputAction ladder;
    private Rigidbody rb;
    private PlayerInput playerInput;
    [SerializeField] private float movementForce = 1f;
    [SerializeField] private float jumpForce = 5f;
    [SerializeField] private float maxSpeed = 5f;
    private Vector3 forceDirection = Vector3.zero;
    [SerializeField] private Camera playerCamera;
    private Vector2 delta;
    private bool controller;
    public float sensitivityController = 50;
    public float sensitivityMouse = 5;
    public int killCount = 0;

    [SerializeField] private GameObject firePoint;

    private Animator animator;
    private bool running = false;

    private bool off = false;
    public bool onladder = false;
    private float upDownSpeed = 2;

    private void Awake()
    {
        animator = GetComponent<Animator>();
        playerInput = GetComponent<PlayerInput>();
        if (playerInput.currentControlScheme == "Gamepad")
        {
            controller = true;
        }
        rb = GetComponent<Rigidbody>();
        inputAsset = this.GetComponent<PlayerInput>().actions;
        player = inputAsset.FindActionMap("Player");
        Cursor.visible = false;
        Cursor.lockState = CursorLockMode.Locked;
    }

    private void OnEnable()
    {
        player.FindAction("Movement").started += DoMove;
        player.FindAction("Movement").canceled += DoRestMove;
        player.FindAction("Run").started += DoRun;
        player.FindAction("Jump").started += DoJump;
        player.FindAction("Crouch").performed += DoCrouch;
        player.FindAction("Crouch").canceled += DoResetCrouch;
        player.FindAction("Look").performed += DoLook;
        player.FindAction("Look").canceled += DoResetLook;
        movement = player.FindAction("Movement");
        ladder = player.FindAction("Ladder");
        player.Enable();
    }

    private void OnDisable()
    {
        player.FindAction("Movement").started -= DoMove;
        player.FindAction("Movement").canceled -= DoRestMove;
        player.FindAction("Run").started -= DoRun;
        player.FindAction("Jump").started -= DoJump;
        player.FindAction("Crouch").performed -= DoCrouch;
        player.FindAction("Crouch").canceled -= DoResetCrouch;
        player.FindAction("Look").performed -= DoLook;
        player.FindAction("Look").canceled -= DoResetLook;
        player.Disable();
    }

    private void DoClimb(InputAction.CallbackContext obj)
    {

    }

    private void DoMove(InputAction.CallbackContext obj)
    {
        SoundManager.instance.WalkingSound();
        animator.SetBool("IsWalking", true);
    }

    private void DoRestMove(InputAction.CallbackContext obj)
    {
        animator.SetBool("IsWalking", false);
    }

    private void DoRun(InputAction.CallbackContext obj)
    {
        running = running ? false : true;
        if (running)
        {
            animator.SetBool("IsRunning", true);
            maxSpeed = 10;
        }
        else
        {
            animator.SetBool("IsRunning", false);
            maxSpeed = 5;
        }
    }

    private void DoLook(InputAction.CallbackContext context)
    {
        delta = context.ReadValue<Vector2>();
        if (!controller)
        {
            float pan = playerCamera.transform.eulerAngles.y;
            float tilt = playerCamera.transform.eulerAngles.x;

            pan += delta.x * Time.deltaTime * sensitivityMouse;
            tilt += -delta.y * Time.deltaTime * sensitivityMouse;

            tilt = (tilt > 180) ? tilt - 360 : tilt;

            tilt = Mathf.Clamp(tilt, -90, 30);

            gameObject.transform.localRotation = Quaternion.Euler(0, pan, 0);
            playerCamera.transform.localRotation = Quaternion.Euler(tilt, 0, 0);
            firePoint.transform.localRotation = Quaternion.Euler(tilt, 0, 0);
        }
    }

    private void Update()
    {
        if (controller && !off)
        {
            float pan = playerCamera.transform.eulerAngles.y;
            float tilt = playerCamera.transform.eulerAngles.x;

            pan += delta.x * Time.deltaTime * sensitivityController;
            tilt += -delta.y * Time.deltaTime * sensitivityController;

            tilt = (tilt > 180) ? tilt - 360 : tilt;

            tilt = Mathf.Clamp(tilt, -90, 30);

            gameObject.transform.localRotation = Quaternion.Euler(0, pan, 0);
            playerCamera.transform.localRotation = Quaternion.Euler(tilt, 0, 0);
            firePoint.transform.localRotation = Quaternion.Euler(tilt, 0, 0);
        }
    }

    private void DoResetLook(InputAction.CallbackContext obj)
    {
        delta.x = 0;
        delta.y = 0;
    }

    private void DoCrouch(InputAction.CallbackContext obj)
    {
        GetComponent<CapsuleCollider>().height = 1.3f;
        GetComponent<CapsuleCollider>().center = new Vector3(0, 0.5f, 0);
    }

    private void DoResetCrouch(InputAction.CallbackContext obj)
    {
        GetComponent<CapsuleCollider>().height = 1.85f;
        GetComponent<CapsuleCollider>().center = new Vector3(0, 0.95f, 0);
    }

    private void FixedUpdate()
    {
        if (!off && !onladder)
        {
            forceDirection += movement.ReadValue<Vector2>().x * GetCameraRight(playerCamera) * movementForce;
            forceDirection += movement.ReadValue<Vector2>().y * GetCameraForward(playerCamera) * movementForce;
            rb.AddForce(forceDirection, ForceMode.Impulse);
            forceDirection = Vector3.zero;

            if (rb.velocity.y < 0f)
            {
                rb.velocity -= Vector3.down * Physics.gravity.y * Time.fixedDeltaTime;
            }

            Vector3 horizontalVelocity = rb.velocity;
            horizontalVelocity.y = 0;
            if (horizontalVelocity.sqrMagnitude > maxSpeed * maxSpeed)
            {
                rb.velocity = horizontalVelocity.normalized * maxSpeed + Vector3.up * rb.velocity.y;
            }
        }
        if (!off && onladder)
        {
            Vector2 upDown = ladder.ReadValue<Vector2>();
            if (onladder)
            {
                if (upDown.y >= 0.2)
                {
                    transform.position += Vector3.up / upDownSpeed;
                }
                if (upDown.y <= -0.2)
                {
                    transform.position += Vector3.down / upDownSpeed;
                }
            }
        }
    }

    private void DoJump(InputAction.CallbackContext obj)
    {
        if (IsGrounded())
        {
            animator.SetTrigger("IsJumping");
            forceDirection += Vector3.up * jumpForce;
        }
    }

    private bool IsGrounded()
    {
        Ray ray = new Ray(this.transform.position + Vector3.up * 0.25f, Vector3.down);
        if (Physics.Raycast(ray, out RaycastHit hit, 0.5f))
            return true;
        else
            return false;
    }

    private Vector3 GetCameraForward(Camera playerCamera)
    {
        Vector3 forward = playerCamera.transform.forward;
        forward.y = 0;
        return forward.normalized;
    }

    private Vector3 GetCameraRight(Camera playerCamera)
    {
        Vector3 right = playerCamera.transform.right;
        right.y = 0;
        return right.normalized;
    }

    public void TurnOff(bool turnOff)
    {
        if (turnOff)
        {
            player.FindAction("Movement").started -= DoMove;
            player.FindAction("Movement").canceled -= DoRestMove;
            player.FindAction("Run").started -= DoRun;
            player.FindAction("Jump").started -= DoJump;
            player.FindAction("Crouch").performed -= DoCrouch;
            player.FindAction("Crouch").canceled -= DoResetCrouch;
            player.FindAction("Look").performed -= DoLook;
            player.FindAction("Look").canceled -= DoResetLook;
            off = true;
        }
        else
        {
            player.FindAction("Movement").started += DoMove;
            player.FindAction("Movement").canceled += DoRestMove;
            player.FindAction("Run").started += DoRun;
            player.FindAction("Jump").started += DoJump;
            player.FindAction("Crouch").performed += DoCrouch;
            player.FindAction("Crouch").canceled += DoResetCrouch;
            player.FindAction("Look").performed += DoLook;
            player.FindAction("Look").canceled += DoResetLook;
            off = false;
        }
    }
}

Controller Support

Met behulp van het new input system van unity heb ik ervoor gezorgd dat je niet alleen met toetsenbord en muis kan spelen maar ook met controller. Dit is de action map van de player. Hierin staam alle knoppen die je kan gebruiken voor het bewegen van de player. Bijvoorbeeld je kan de toetsen WASD gebruiken om te lopen, maar ook de linker joystick kan je lopen,

C#
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.InputSystem;

public class GameManager : MonoBehaviour
{
    public static GameManager instance;
    public List<PlayerController> players = new List<PlayerController>();
    private PlayerController[] allPplayers;
    private bool gamestart = false;
    private bool gameover = false;
    private float timer = 0;

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else if (instance != null)
        {
            Destroy(gameObject);
        }
    }

    private void Start()
    {
        PlayerInputManager.instance.onPlayerJoined += SetID;
        PlayerInputManager.instance.onPlayerLeft += RemoveID;
    }

    private void SetID(PlayerInput obj)
    {
        obj.gameObject.GetComponent<PlayerController>().ID = FindObjectsOfType<PlayerController>().Length - 1;
        players.Add(obj.gameObject.GetComponent<PlayerController>());
    }
    private void RemoveID(PlayerInput obj)
    {
        players.Remove(obj.gameObject.GetComponent<PlayerController>());
    }

    public void GiveKill(int killPlayerId)
    {
        allPplayers = players.ToArray();
        allPplayers[killPlayerId].killCount++;
    }

    private void Update()
    {
        if (players.Count >= 2&& !gamestart)
        {
            Timer.instance.timerIsRunning = true;
            gamestart = true;
        }
        if (gameover)
        {
            timer += Time.deltaTime;
        }
        if (timer >= 5)
        {
            SceneManager.LoadScene("StartMenu");
        }
    }

    public void GameEnd()
    {
        allPplayers = players.ToArray();
        int mostkills = 0, mostkillsID = -1;
        SoundManager.instance.EndGameSound();
        Debug.Log("game end");
        for (int i = 0; i < allPplayers.Length; i++)
        {
            if (allPplayers[i].killCount >= mostkills)
            {
                mostkills = allPplayers[i].killCount;
                mostkillsID = allPplayers[i].ID;
            }
        }
        if (mostkills == 1)
        {
            Debug.Log("player " + mostkillsID + " wins with " + mostkills + "kill");
        }
        else
        {
            Debug.Log("player " + mostkillsID + " wins with " + mostkills + "kills");
        }
    }
}

Game Manager

Ik heb een Game Manager gemaakt. Als het spel start en je joined het spel, dan krijg je een player id toegewezen. Verlaat je het spel , dan wordt player id verwijderd. Als er meer dan 2 players in het spel zijn start het spel. Als een player een kill maakt wordt hij de kill toegewezen door de Game Manger. Dit gebeurd als een player dood gaat. Dan kijkt hij door wie hij voor het laatst is gehit en dan geeft hij de player id door aan de Game Manager. Die kijkt bij welke player dat id hoort en geeft de een punt er bij. Als de timer van de game op nul staat wordt GameEnd aangeroepen, Daarin wordt de game stop gezet en bepaald wie de meeste kills heeft . Die speler heeft gewonnen.

Player Health

Ik heb ook een simpel Player Health script gemaakt. Als de player schade krijgt, dan wordt HealthChange aangeroepen met het nummer wat er af moet. Daarna wordt er gekeken of de health van de player nul is. Zo ja wordt er “is Dead” op true gezet en de player id wordt meegegeven aan de Game Manager en PayerDead  wordt aangeroepen

C#
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerHealth : MonoBehaviour
{
    public float playerHealth = 100;
    public bool isDead = false;
    public int hitPlayerID;
    private int killID;

    public void HealthChange(float Value)
    {
        if (hitPlayerID == this.gameObject.GetComponent<PlayerController>().ID) return;
        playerHealth += Value;
        if (playerHealth <= 0)
        {
            isDead = true;
            killID = hitPlayerID;
            GameManager.instance.GiveKill(killID);
            PlayerDead();
        }
    }

    private void Update()
    {
        if (playerHealth <= 0)
        {
            isDead = true;
            PlayerDead();
        }
    }

    private void PlayerDead()
    {

    }
}
C#
using TMPro;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Timer : MonoBehaviour
{
    public static Timer instance;
    [SerializeField] public float timer = 0;
    public bool timerIsRunning = false;
    public List<TMP_Text> timeText;

    private void Awake()
    {
        instance = this;
    }

    void Update()
    {
        if (timer <= 0 && timerIsRunning)
        {
            timerIsRunning = false;
            timer = 0;
            GameManager.instance.GameEnd();
            return;
        }
        else if (timerIsRunning)
        {
            timer -= Time.deltaTime;
            DisplayTime(timer);
        }
    }
    void DisplayTime(float timeToDisplay)
    {
        TMP_Text[] timertexts = timeText.ToArray();
        float minutes = Mathf.FloorToInt(timeToDisplay / 60);
        float seconds = Mathf.FloorToInt(timeToDisplay % 60);
        for (int i = 0; i < timertexts.Length; i++)
        {
            timertexts[i].text = string.Format("{0:00}:{1:00}", minutes, seconds);
        }
    }
}

Timer

Deze timer gaat aftellen als de game gestart is. Na een bepaalde tijd zet hij de timer stop en roept de GameEnd aan. Als de timer bezig is met aftellen, dan update hij zelf de timer op je scherm naar minuten en seconden.

Spawn points

Ik heb negen spawn points gemaakt waar de players kunnen spawnen. Als een player bij zo punt in de buurt is dan gaat de spawn point uit.

Als een player dood gaat wordt er een willekeurige spawn point gekozen uit een lijst van alle spawn points die aan staan.

C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RespawnSystem : MonoBehaviour
{
    [SerializeField] private GameObject[] respawn;

    private void Update()
    {
        int random = Random.Range(0, respawn.Length);
        for (int i = 0; i < FindObjectsOfType<PlayerHealth>().Length; i++)
        {
            if (FindObjectsOfType<PlayerHealth>()[i].isDead)
            {
                if (respawn[random].GetComponent<SpawnPoint>().canSpawn)
                {
                    FindObjectsOfType<PlayerHealth>()[i].transform.position = respawn[random].transform.position;
                    FindObjectsOfType<PlayerHealth>()[i].playerHealth = 100;
                    FindObjectsOfType<PlayerHealth>()[i].isDead = false;
                }

            }
        }
    }
}