TANKS!

This examples based on Tanks! Tutorial.

This example was made by using CSharpParser to parse the following script:

  • CameraControl.cs
  • GameManager.cs
  • TankManager.cs
  • ShellExplosion.cs
  • TankHealth.cs
  • TankMovement.cs
  • TankShooting.cs
  • UIDirectionControl.cs

Original Script:

using UnityEngine;

namespace Complete
{
    public class CameraControl : MonoBehaviour
    {
        public float m_DampTime = 0.2f;                 // Approximate time for the camera to refocus.
        public float m_ScreenEdgeBuffer = 4f;           // Space between the top/bottom most target and the screen edge.
        public float m_MinSize = 6.5f;                  // The smallest orthographic size the camera can be.
        [HideInInspector] public Transform[] m_Targets; // All the targets the camera needs to encompass.


        private Camera m_Camera;                        // Used for referencing the camera.
        private float m_ZoomSpeed;                      // Reference speed for the smooth damping of the orthographic size.
        private Vector3 m_MoveVelocity;                 // Reference velocity for the smooth damping of the position.
        private Vector3 m_DesiredPosition;              // The position the camera is moving towards.


        private void Awake ()
        {
            m_Camera = GetComponentInChildren<Camera> ();
        }


        private void FixedUpdate ()
        {
            // Move the camera towards a desired position.
            Move ();

            // Change the size of the camera based.
            Zoom ();
        }


        private void Move ()
        {
            // Find the average position of the targets.
            FindAveragePosition ();

            // Smoothly transition to that position.
            transform.position = Vector3.SmoothDamp(transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
        }


        private void FindAveragePosition ()
        {
            Vector3 averagePos = new Vector3 ();
            int numTargets = 0;

            // Go through all the targets and add their positions together.
            for (int i = 0; i < m_Targets.Length; i++)
            {
                // If the target isn't active, go on to the next one.
                if (!m_Targets[i].gameObject.activeSelf)
                    continue;

                // Add to the average and increment the number of targets in the average.
                averagePos += m_Targets[i].position;
                numTargets++;
            }

            // If there are targets divide the sum of the positions by the number of them to find the average.
            if (numTargets > 0)
                averagePos /= numTargets;

            // Keep the same y value.
            averagePos.y = transform.position.y;

            // The desired position is the average position;
            m_DesiredPosition = averagePos;
        }


        private void Zoom ()
        {
            // Find the required size based on the desired position and smoothly transition to that size.
            float requiredSize = FindRequiredSize();
            m_Camera.orthographicSize = Mathf.SmoothDamp (m_Camera.orthographicSize, requiredSize, ref m_ZoomSpeed, m_DampTime);
        }


        private float FindRequiredSize ()
        {
            // Find the position the camera rig is moving towards in its local space.
            Vector3 desiredLocalPos = transform.InverseTransformPoint(m_DesiredPosition);

            // Start the camera's size calculation at zero.
            float size = 0f;

            // Go through all the targets...
            for (int i = 0; i < m_Targets.Length; i++)
            {
                // ... and if they aren't active continue on to the next target.
                if (!m_Targets[i].gameObject.activeSelf)
                    continue;

                // Otherwise, find the position of the target in the camera's local space.
                Vector3 targetLocalPos = transform.InverseTransformPoint(m_Targets[i].position);

                // Find the position of the target from the desired position of the camera's local space.
                Vector3 desiredPosToTarget = targetLocalPos - desiredLocalPos;

                // Choose the largest out of the current size and the distance of the tank 'up' or 'down' from the camera.
                size = Mathf.Max(size, Mathf.Abs(desiredPosToTarget.y));

                // Choose the largest out of the current size and the calculated size based on the tank being to the left or right of the camera.
                size = Mathf.Max(size, Mathf.Abs(desiredPosToTarget.x) / m_Camera.aspect);
            }

            // Add the edge buffer to the size.
            size += m_ScreenEdgeBuffer;

            // Make sure the camera's size isn't below the minimum.
            size = Mathf.Max (size, m_MinSize);

            return size;
        }


        public void SetStartPositionAndSize ()
        {
            // Find the desired position.
            FindAveragePosition ();

            // Set the camera's position to the desired position without damping.
            transform.position = m_DesiredPosition;

            // Find and set the required size of the camera.
            m_Camera.orthographicSize = FindRequiredSize ();
        }
    }
}
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

namespace Complete
{
    public class GameManager : MonoBehaviour
    {
        public int m_NumRoundsToWin = 5;            // The number of rounds a single player has to win to win the game.
        public float m_StartDelay = 3f;             // The delay between the start of RoundStarting and RoundPlaying phases.
        public float m_EndDelay = 3f;               // The delay between the end of RoundPlaying and RoundEnding phases.
        public CameraControl m_CameraControl;       // Reference to the CameraControl script for control during different phases.
        public Text m_MessageText;                  // Reference to the overlay Text to display winning text, etc.
        public GameObject m_TankPrefab;             // Reference to the prefab the players will control.
        public TankManager[] m_Tanks;               // A collection of managers for enabling and disabling different aspects of the tanks.

        
        private int m_RoundNumber;                  // Which round the game is currently on.
        private WaitForSeconds m_StartWait;         // Used to have a delay whilst the round starts.
        private WaitForSeconds m_EndWait;           // Used to have a delay whilst the round or game ends.
        private TankManager m_RoundWinner;          // Reference to the winner of the current round.  Used to make an announcement of who won.
        private TankManager m_GameWinner;           // Reference to the winner of the game.  Used to make an announcement of who won.


        private void Start()
        {
            // Create the delays so they only have to be made once.
            m_StartWait = new WaitForSeconds (m_StartDelay);
            m_EndWait = new WaitForSeconds (m_EndDelay);

            SpawnAllTanks();
            SetCameraTargets();

            // Once the tanks have been created and the camera is using them as targets, start the game.
            StartCoroutine (GameLoop ());
        }


        private void SpawnAllTanks()
        {
            // For all the tanks...
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                // ... create them, set their player number and references needed for control.
                m_Tanks[i].m_Instance =
                    Instantiate(m_TankPrefab, m_Tanks[i].m_SpawnPoint.position, m_Tanks[i].m_SpawnPoint.rotation) as GameObject;
                m_Tanks[i].m_PlayerNumber = i + 1;
                m_Tanks[i].Setup();
            }
        }


        private void SetCameraTargets()
        {
            // Create a collection of transforms the same size as the number of tanks.
            Transform[] targets = new Transform[m_Tanks.Length];

            // For each of these transforms...
            for (int i = 0; i < targets.Length; i++)
            {
                // ... set it to the appropriate tank transform.
                targets[i] = m_Tanks[i].m_Instance.transform;
            }

            // These are the targets the camera should follow.
            m_CameraControl.m_Targets = targets;
        }


        // This is called from start and will run each phase of the game one after another.
        private IEnumerator GameLoop ()
        {
            // Start off by running the 'RoundStarting' coroutine but don't return until it's finished.
            yield return StartCoroutine (RoundStarting ());

            // Once the 'RoundStarting' coroutine is finished, run the 'RoundPlaying' coroutine but don't return until it's finished.
            yield return StartCoroutine (RoundPlaying());

            // Once execution has returned here, run the 'RoundEnding' coroutine, again don't return until it's finished.
            yield return StartCoroutine (RoundEnding());

            // This code is not run until 'RoundEnding' has finished.  At which point, check if a game winner has been found.
            if (m_GameWinner != null)
            {
                // If there is a game winner, restart the level.
                Application.LoadLevel (Application.loadedLevel);
            }
            else
            {
                // If there isn't a winner yet, restart this coroutine so the loop continues.
                // Note that this coroutine doesn't yield.  This means that the current version of the GameLoop will end.
                StartCoroutine (GameLoop ());
            }
        }


        private IEnumerator RoundStarting ()
        {
            // As soon as the round starts reset the tanks and make sure they can't move.
            ResetAllTanks ();
            DisableTankControl ();

            // Snap the camera's zoom and position to something appropriate for the reset tanks.
            m_CameraControl.SetStartPositionAndSize ();

            // Increment the round number and display text showing the players what round it is.
            m_RoundNumber++;
            m_MessageText.text = "ROUND " + m_RoundNumber;

            // Wait for the specified length of time until yielding control back to the game loop.
            yield return m_StartWait;
        }


        private IEnumerator RoundPlaying ()
        {
            // As soon as the round begins playing let the players control the tanks.
            EnableTankControl ();

            // Clear the text from the screen.
            m_MessageText.text = string.Empty;

            // While there is not one tank left...
            while (!OneTankLeft())
            {
                // ... return on the next frame.
                yield return null;
            }
        }


        private IEnumerator RoundEnding ()
        {
            // Stop tanks from moving.
            DisableTankControl ();

            // Clear the winner from the previous round.
            m_RoundWinner = null;

            // See if there is a winner now the round is over.
            m_RoundWinner = GetRoundWinner ();

            // If there is a winner, increment their score.
            if (m_RoundWinner != null)
                m_RoundWinner.m_Wins++;

            // Now the winner's score has been incremented, see if someone has one the game.
            m_GameWinner = GetGameWinner ();

            // Get a message based on the scores and whether or not there is a game winner and display it.
            string message = EndMessage ();
            m_MessageText.text = message;

            // Wait for the specified length of time until yielding control back to the game loop.
            yield return m_EndWait;
        }


        // This is used to check if there is one or fewer tanks remaining and thus the round should end.
        private bool OneTankLeft()
        {
            // Start the count of tanks left at zero.
            int numTanksLeft = 0;

            // Go through all the tanks...
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                // ... and if they are active, increment the counter.
                if (m_Tanks[i].m_Instance.activeSelf)
                    numTanksLeft++;
            }

            // If there are one or fewer tanks remaining return true, otherwise return false.
            return numTanksLeft <= 1;
        }
        
        
        // This function is to find out if there is a winner of the round.
        // This function is called with the assumption that 1 or fewer tanks are currently active.
        private TankManager GetRoundWinner()
        {
            // Go through all the tanks...
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                // ... and if one of them is active, it is the winner so return it.
                if (m_Tanks[i].m_Instance.activeSelf)
                    return m_Tanks[i];
            }

            // If none of the tanks are active it is a draw so return null.
            return null;
        }


        // This function is to find out if there is a winner of the game.
        private TankManager GetGameWinner()
        {
            // Go through all the tanks...
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                // ... and if one of them has enough rounds to win the game, return it.
                if (m_Tanks[i].m_Wins == m_NumRoundsToWin)
                    return m_Tanks[i];
            }

            // If no tanks have enough rounds to win, return null.
            return null;
        }


        // Returns a string message to display at the end of each round.
        private string EndMessage()
        {
            // By default when a round ends there are no winners so the default end message is a draw.
            string message = "DRAW!";

            // If there is a winner then change the message to reflect that.
            if (m_RoundWinner != null)
                message = m_RoundWinner.m_ColoredPlayerText + " WINS THE ROUND!";

            // Add some line breaks after the initial message.
            message += "\n\n\n\n";

            // Go through all the tanks and add each of their scores to the message.
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                message += m_Tanks[i].m_ColoredPlayerText + ": " + m_Tanks[i].m_Wins + " WINS\n";
            }

            // If there is a game winner, change the entire message to reflect that.
            if (m_GameWinner != null)
                message = m_GameWinner.m_ColoredPlayerText + " WINS THE GAME!";

            return message;
        }


        // This function is used to turn all the tanks back on and reset their positions and properties.
        private void ResetAllTanks()
        {
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                m_Tanks[i].Reset();
            }
        }


        private void EnableTankControl()
        {
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                m_Tanks[i].EnableControl();
            }
        }


        private void DisableTankControl()
        {
            for (int i = 0; i < m_Tanks.Length; i++)
            {
                m_Tanks[i].DisableControl();
            }
        }
    }
}
using System;
using UnityEngine;

namespace Complete
{
    [Serializable]
    public class TankManager
    {
        // This class is to manage various settings on a tank.
        // It works with the GameManager class to control how the tanks behave
        // and whether or not players have control of their tank in the 
        // different phases of the game.

        public Color m_PlayerColor;                             // This is the color this tank will be tinted.
        public Transform m_SpawnPoint;                          // The position and direction the tank will have when it spawns.
        [HideInInspector] public int m_PlayerNumber;            // This specifies which player this the manager for.
        [HideInInspector] public string m_ColoredPlayerText;    // A string that represents the player with their number colored to match their tank.
        [HideInInspector] public GameObject m_Instance;         // A reference to the instance of the tank when it is created.
        [HideInInspector] public int m_Wins;                    // The number of wins this player has so far.
        

        private TankMovement m_Movement;                        // Reference to tank's movement script, used to disable and enable control.
        private TankShooting m_Shooting;                        // Reference to tank's shooting script, used to disable and enable control.
        private GameObject m_CanvasGameObject;                  // Used to disable the world space UI during the Starting and Ending phases of each round.


        public void Setup ()
        {
            // Get references to the components.
            m_Movement = m_Instance.GetComponent<TankMovement> ();
            m_Shooting = m_Instance.GetComponent<TankShooting> ();
            m_CanvasGameObject = m_Instance.GetComponentInChildren<Canvas> ().gameObject;

            // Set the player numbers to be consistent across the scripts.
            m_Movement.m_PlayerNumber = m_PlayerNumber;
            m_Shooting.m_PlayerNumber = m_PlayerNumber;

            // Create a string using the correct color that says 'PLAYER 1' etc based on the tank's color and the player's number.
            m_ColoredPlayerText = "<color=#" + ColorUtility.ToHtmlStringRGB(m_PlayerColor) + ">PLAYER " + m_PlayerNumber + "</color>";

            // Get all of the renderers of the tank.
            MeshRenderer[] renderers = m_Instance.GetComponentsInChildren<MeshRenderer> ();

            // Go through all the renderers...
            for (int i = 0; i < renderers.Length; i++)
            {
                // ... set their material color to the color specific to this tank.
                renderers[i].material.color = m_PlayerColor;
            }
        }


        // Used during the phases of the game where the player shouldn't be able to control their tank.
        public void DisableControl ()
        {
            m_Movement.enabled = false;
            m_Shooting.enabled = false;

            m_CanvasGameObject.SetActive (false);
        }


        // Used during the phases of the game where the player should be able to control their tank.
        public void EnableControl ()
        {
            m_Movement.enabled = true;
            m_Shooting.enabled = true;

            m_CanvasGameObject.SetActive (true);
        }


        // Used at the start of each round to put the tank into it's default state.
        public void Reset ()
        {
            m_Instance.transform.position = m_SpawnPoint.position;
            m_Instance.transform.rotation = m_SpawnPoint.rotation;

            m_Instance.SetActive (false);
            m_Instance.SetActive (true);
        }
    }
}
using UnityEngine;

namespace Complete
{
    public class ShellExplosion : MonoBehaviour
    {
        public LayerMask m_TankMask;                        // Used to filter what the explosion affects, this should be set to "Players".
        public ParticleSystem m_ExplosionParticles;         // Reference to the particles that will play on explosion.
        public AudioSource m_ExplosionAudio;                // Reference to the audio that will play on explosion.
        public float m_MaxDamage = 100f;                    // The amount of damage done if the explosion is centred on a tank.
        public float m_ExplosionForce = 1000f;              // The amount of force added to a tank at the centre of the explosion.
        public float m_MaxLifeTime = 2f;                    // The time in seconds before the shell is removed.
        public float m_ExplosionRadius = 5f;                // The maximum distance away from the explosion tanks can be and are still affected.


        private void Start ()
        {
            // If it isn't destroyed by then, destroy the shell after it's lifetime.
            Destroy (gameObject, m_MaxLifeTime);
        }


        private void OnTriggerEnter (Collider other)
        {
      // Collect all the colliders in a sphere from the shell's current position to a radius of the explosion radius.
            Collider[] colliders = Physics.OverlapSphere (transform.position, m_ExplosionRadius, m_TankMask);

            // Go through all the colliders...
            for (int i = 0; i < colliders.Length; i++)
            {
                // ... and find their rigidbody.
                Rigidbody targetRigidbody = colliders[i].GetComponent<Rigidbody> ();

                // If they don't have a rigidbody, go on to the next collider.
                if (!targetRigidbody)
                    continue;

                // Add an explosion force.
                targetRigidbody.AddExplosionForce (m_ExplosionForce, transform.position, m_ExplosionRadius);

                // Find the TankHealth script associated with the rigidbody.
                TankHealth targetHealth = targetRigidbody.GetComponent<TankHealth> ();

                // If there is no TankHealth script attached to the gameobject, go on to the next collider.
                if (!targetHealth)
                    continue;

                // Calculate the amount of damage the target should take based on it's distance from the shell.
                float damage = CalculateDamage (targetRigidbody.position);

                // Deal this damage to the tank.
                targetHealth.TakeDamage (damage);
            }

            // Unparent the particles from the shell.
            m_ExplosionParticles.transform.parent = null;

            // Play the particle system.
            m_ExplosionParticles.Play();

            // Play the explosion sound effect.
            m_ExplosionAudio.Play();

            // Once the particles have finished, destroy the gameobject they are on.
            Destroy (m_ExplosionParticles.gameObject, m_ExplosionParticles.duration);

            // Destroy the shell.
            Destroy (gameObject);
        }


        private float CalculateDamage (Vector3 targetPosition)
        {
            // Create a vector from the shell to the target.
            Vector3 explosionToTarget = targetPosition - transform.position;

            // Calculate the distance from the shell to the target.
            float explosionDistance = explosionToTarget.magnitude;

            // Calculate the proportion of the maximum distance (the explosionRadius) the target is away.
            float relativeDistance = (m_ExplosionRadius - explosionDistance) / m_ExplosionRadius;

            // Calculate damage as this proportion of the maximum possible damage.
            float damage = relativeDistance * m_MaxDamage;

            // Make sure that the minimum damage is always 0.
            damage = Mathf.Max (0f, damage);

            return damage;
        }
    }
}
using UnityEngine;
using UnityEngine.UI;

namespace Complete
{
    public class TankHealth : MonoBehaviour
    {
        public float m_StartingHealth = 100f;               // The amount of health each tank starts with.
        public Slider m_Slider;                             // The slider to represent how much health the tank currently has.
        public Image m_FillImage;                           // The image component of the slider.
        public Color m_FullHealthColor = Color.green;       // The color the health bar will be when on full health.
        public Color m_ZeroHealthColor = Color.red;         // The color the health bar will be when on no health.
        public GameObject m_ExplosionPrefab;                // A prefab that will be instantiated in Awake, then used whenever the tank dies.
        
        
        private AudioSource m_ExplosionAudio;               // The audio source to play when the tank explodes.
        private ParticleSystem m_ExplosionParticles;        // The particle system the will play when the tank is destroyed.
        private float m_CurrentHealth;                      // How much health the tank currently has.
        private bool m_Dead;                                // Has the tank been reduced beyond zero health yet?


        private void Awake ()
        {
            // Instantiate the explosion prefab and get a reference to the particle system on it.
            m_ExplosionParticles = Instantiate (m_ExplosionPrefab).GetComponent<ParticleSystem> ();

            // Get a reference to the audio source on the instantiated prefab.
            m_ExplosionAudio = m_ExplosionParticles.GetComponent<AudioSource> ();

            // Disable the prefab so it can be activated when it's required.
            m_ExplosionParticles.gameObject.SetActive (false);
        }


        private void OnEnable()
        {
            // When the tank is enabled, reset the tank's health and whether or not it's dead.
            m_CurrentHealth = m_StartingHealth;
            m_Dead = false;

            // Update the health slider's value and color.
            SetHealthUI();
        }


        public void TakeDamage (float amount)
        {
            // Reduce current health by the amount of damage done.
            m_CurrentHealth -= amount;

            // Change the UI elements appropriately.
            SetHealthUI ();

            // If the current health is at or below zero and it has not yet been registered, call OnDeath.
            if (m_CurrentHealth <= 0f && !m_Dead)
            {
                OnDeath ();
            }
        }


        private void SetHealthUI ()
        {
            // Set the slider's value appropriately.
            m_Slider.value = m_CurrentHealth;

            // Interpolate the color of the bar between the choosen colours based on the current percentage of the starting health.
            m_FillImage.color = Color.Lerp (m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth);
        }


        private void OnDeath ()
        {
            // Set the flag so that this function is only called once.
            m_Dead = true;

            // Move the instantiated explosion prefab to the tank's position and turn it on.
            m_ExplosionParticles.transform.position = transform.position;
            m_ExplosionParticles.gameObject.SetActive (true);

            // Play the particle system of the tank exploding.
            m_ExplosionParticles.Play ();

            // Play the tank explosion sound effect.
            m_ExplosionAudio.Play();

            // Turn the tank off.
            gameObject.SetActive (false);
        }
    }
}
using UnityEngine;

namespace Complete
{
    public class TankMovement : MonoBehaviour
    {
        public int m_PlayerNumber = 1;              // Used to identify which tank belongs to which player.  This is set by this tank's manager.
        public float m_Speed = 12f;                 // How fast the tank moves forward and back.
        public float m_TurnSpeed = 180f;            // How fast the tank turns in degrees per second.
        public AudioSource m_MovementAudio;         // Reference to the audio source used to play engine sounds. NB: different to the shooting audio source.
        public AudioClip m_EngineIdling;            // Audio to play when the tank isn't moving.
        public AudioClip m_EngineDriving;           // Audio to play when the tank is moving.
    public float m_PitchRange = 0.2f;           // The amount by which the pitch of the engine noises can vary.


        private string m_MovementAxisName;          // The name of the input axis for moving forward and back.
        private string m_TurnAxisName;              // The name of the input axis for turning.
        private Rigidbody m_Rigidbody;              // Reference used to move the tank.
        private float m_MovementInputValue;         // The current value of the movement input.
        private float m_TurnInputValue;             // The current value of the turn input.
        private float m_OriginalPitch;              // The pitch of the audio source at the start of the scene.


        private void Awake ()
        {
            m_Rigidbody = GetComponent<Rigidbody> ();
        }


        private void OnEnable ()
        {
            // When the tank is turned on, make sure it's not kinematic.
            m_Rigidbody.isKinematic = false;

            // Also reset the input values.
            m_MovementInputValue = 0f;
            m_TurnInputValue = 0f;
        }


        private void OnDisable ()
        {
            // When the tank is turned off, set it to kinematic so it stops moving.
            m_Rigidbody.isKinematic = true;
        }


        private void Start ()
        {
            // The axes names are based on player number.
            m_MovementAxisName = "Vertical" + m_PlayerNumber;
            m_TurnAxisName = "Horizontal" + m_PlayerNumber;

            // Store the original pitch of the audio source.
            m_OriginalPitch = m_MovementAudio.pitch;
        }


        private void Update ()
        {
            // Store the value of both input axes.
            m_MovementInputValue = Input.GetAxis (m_MovementAxisName);
            m_TurnInputValue = Input.GetAxis (m_TurnAxisName);

            EngineAudio ();
        }


        private void EngineAudio ()
        {
            // If there is no input (the tank is stationary)...
            if (Mathf.Abs (m_MovementInputValue) < 0.1f && Mathf.Abs (m_TurnInputValue) < 0.1f)
            {
                // ... and if the audio source is currently playing the driving clip...
                if (m_MovementAudio.clip == m_EngineDriving)
                {
                    // ... change the clip to idling and play it.
                    m_MovementAudio.clip = m_EngineIdling;
                    m_MovementAudio.pitch = Random.Range (m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
                    m_MovementAudio.Play ();
                }
            }
            else
            {
                // Otherwise if the tank is moving and if the idling clip is currently playing...
                if (m_MovementAudio.clip == m_EngineIdling)
                {
                    // ... change the clip to driving and play.
                    m_MovementAudio.clip = m_EngineDriving;
                    m_MovementAudio.pitch = Random.Range(m_OriginalPitch - m_PitchRange, m_OriginalPitch + m_PitchRange);
                    m_MovementAudio.Play();
                }
            }
        }


        private void FixedUpdate ()
        {
            // Adjust the rigidbodies position and orientation in FixedUpdate.
            Move ();
            Turn ();
        }


        private void Move ()
        {
            // Create a vector in the direction the tank is facing with a magnitude based on the input, speed and the time between frames.
            Vector3 movement = transform.forward * m_MovementInputValue * m_Speed * Time.deltaTime;

            // Apply this movement to the rigidbody's position.
            m_Rigidbody.MovePosition(m_Rigidbody.position + movement);
        }


        private void Turn ()
        {
            // Determine the number of degrees to be turned based on the input, speed and time between frames.
            float turn = m_TurnInputValue * m_TurnSpeed * Time.deltaTime;

            // Make this into a rotation in the y axis.
            Quaternion turnRotation = Quaternion.Euler (0f, turn, 0f);

            // Apply this rotation to the rigidbody's rotation.
            m_Rigidbody.MoveRotation (m_Rigidbody.rotation * turnRotation);
        }
    }
}
using UnityEngine;
using UnityEngine.UI;

namespace Complete
{
    public class TankShooting : MonoBehaviour
    {
        public int m_PlayerNumber = 1;              // Used to identify the different players.
        public Rigidbody m_Shell;                   // Prefab of the shell.
        public Transform m_FireTransform;           // A child of the tank where the shells are spawned.
        public Slider m_AimSlider;                  // A child of the tank that displays the current launch force.
        public AudioSource m_ShootingAudio;         // Reference to the audio source used to play the shooting audio. NB: different to the movement audio source.
        public AudioClip m_ChargingClip;            // Audio that plays when each shot is charging up.
        public AudioClip m_FireClip;                // Audio that plays when each shot is fired.
        public float m_MinLaunchForce = 15f;        // The force given to the shell if the fire button is not held.
        public float m_MaxLaunchForce = 30f;        // The force given to the shell if the fire button is held for the max charge time.
        public float m_MaxChargeTime = 0.75f;       // How long the shell can charge for before it is fired at max force.


        private string m_FireButton;                // The input axis that is used for launching shells.
        private float m_CurrentLaunchForce;         // The force that will be given to the shell when the fire button is released.
        private float m_ChargeSpeed;                // How fast the launch force increases, based on the max charge time.
        private bool m_Fired;                       // Whether or not the shell has been launched with this button press.


        private void OnEnable()
        {
            // When the tank is turned on, reset the launch force and the UI
            m_CurrentLaunchForce = m_MinLaunchForce;
            m_AimSlider.value = m_MinLaunchForce;
        }


        private void Start ()
        {
            // The fire axis is based on the player number.
            m_FireButton = "Fire" + m_PlayerNumber;

            // The rate that the launch force charges up is the range of possible forces by the max charge time.
            m_ChargeSpeed = (m_MaxLaunchForce - m_MinLaunchForce) / m_MaxChargeTime;
        }


        private void Update ()
        {
            // The slider should have a default value of the minimum launch force.
            m_AimSlider.value = m_MinLaunchForce;

            // If the max force has been exceeded and the shell hasn't yet been launched...
            if (m_CurrentLaunchForce >= m_MaxLaunchForce && !m_Fired)
            {
                // ... use the max force and launch the shell.
                m_CurrentLaunchForce = m_MaxLaunchForce;
                Fire ();
            }
            // Otherwise, if the fire button has just started being pressed...
            else if (Input.GetButtonDown (m_FireButton))
            {
                // ... reset the fired flag and reset the launch force.
                m_Fired = false;
                m_CurrentLaunchForce = m_MinLaunchForce;

                // Change the clip to the charging clip and start it playing.
                m_ShootingAudio.clip = m_ChargingClip;
                m_ShootingAudio.Play ();
            }
            // Otherwise, if the fire button is being held and the shell hasn't been launched yet...
            else if (Input.GetButton (m_FireButton) && !m_Fired)
            {
                // Increment the launch force and update the slider.
                m_CurrentLaunchForce += m_ChargeSpeed * Time.deltaTime;

                m_AimSlider.value = m_CurrentLaunchForce;
            }
            // Otherwise, if the fire button is released and the shell hasn't been launched yet...
            else if (Input.GetButtonUp (m_FireButton) && !m_Fired)
            {
                // ... launch the shell.
                Fire ();
            }
        }


        private void Fire ()
        {
            // Set the fired flag so only Fire is only called once.
            m_Fired = true;

            // Create an instance of the shell and store a reference to it's rigidbody.
            Rigidbody shellInstance =
                Instantiate (m_Shell, m_FireTransform.position, m_FireTransform.rotation) as Rigidbody;

            // Set the shell's velocity to the launch force in the fire position's forward direction.
            shellInstance.velocity = m_CurrentLaunchForce * m_FireTransform.forward; ;

            // Change the clip to the firing clip and play it.
            m_ShootingAudio.clip = m_FireClip;
            m_ShootingAudio.Play ();

            // Reset the launch force.  This is a precaution in case of missing button events.
            m_CurrentLaunchForce = m_MinLaunchForce;
        }
    }
}
using UnityEngine;

namespace Complete
{
    public class UIDirectionControl : MonoBehaviour
    {
        // This class is used to make sure world space UI
        // elements such as the health bar face the correct direction.

        public bool m_UseRelativeRotation = true;       // Use relative rotation should be used for this gameobject?


        private Quaternion m_RelativeRotation;          // The local rotatation at the start of the scene.


        private void Start ()
        {
            m_RelativeRotation = transform.parent.localRotation;
        }


        private void Update ()
        {
            if (m_UseRelativeRotation)
                transform.rotation = m_RelativeRotation;
        }
    }
}

Generated Script:

using UnityEngine;

namespace Complete {
  public class CameraControl : MonoBehaviour {
    public float m_DampTime = 0.2F;
    public float m_ScreenEdgeBuffer = 4F;
    public float m_MinSize = 6.5F;
    [HideInInspector]
    public Transform[] m_Targets = new Transform[0];
    private Camera m_Camera;
    private float m_ZoomSpeed = 0F;
    private Vector3 m_MoveVelocity = Vector3.zero;
    private Vector3 m_DesiredPosition = Vector3.zero;
    private int index;
    private int index1;

    private void Awake() {
      m_Camera = base.GetComponentInChildren<Camera>();
    }

    private void FixedUpdate() {
      Move();
      Zoom();
    }

    private void Move() {
      FindAveragePosition();
      base.transform.position = Vector3.SmoothDamp(base.transform.position, m_DesiredPosition, ref m_MoveVelocity, m_DampTime);
    }

    private void FindAveragePosition() {
      Vector3 averagePos;
      int numTargets = 0;
      averagePos = Vector3.zero;
      for(index = 0; index < m_Targets.Length; index += 1) {
        if(!(m_Targets[index].gameObject.activeSelf)) {
          continue;
        }
        averagePos += m_Targets[index].position;
        numTargets += 1;
      }
      if((numTargets > 0)) {
        averagePos /= numTargets;
      }
      averagePos.y = base.transform.position.y;
      m_DesiredPosition = averagePos;
    }

    private void Zoom() {
      float requiredSize;
      requiredSize = FindRequiredSize();
      m_Camera.orthographicSize = Mathf.SmoothDamp(m_Camera.orthographicSize, requiredSize, ref m_ZoomSpeed, m_DampTime);
    }

    private float FindRequiredSize() {
      Vector3 desiredLocalPos;
      float size = 0F;
      Vector3 targetLocalPos;
      Vector3 desiredPosToTarget;
      desiredLocalPos = base.transform.InverseTransformPoint(m_DesiredPosition);
      for(index1 = 0; index1 < m_Targets.Length; index1 += 1) {
        if(!(m_Targets[index1].gameObject.activeSelf)) {
          continue;
        }
        targetLocalPos = base.transform.InverseTransformPoint(m_Targets[index1].position);
        desiredPosToTarget = (targetLocalPos - desiredLocalPos);
        size = Mathf.Max(size, Mathf.Abs(desiredPosToTarget.y));
        size = Mathf.Max(size, (Mathf.Abs(desiredPosToTarget.x) / m_Camera.aspect));
      }
      size += m_ScreenEdgeBuffer;
      size = Mathf.Max(size, m_MinSize);
      return size;
    }

    public void SetStartPositionAndSize() {
      FindAveragePosition();
      base.transform.position = m_DesiredPosition;
      m_Camera.orthographicSize = FindRequiredSize();
    }
  }
}
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

namespace Complete {
  public class GameManager : MonoBehaviour {
    public int m_NumRoundsToWin = 5;
    public float m_StartDelay = 3F;
    public float m_EndDelay = 3F;
    public Complete.CameraControl m_CameraControl;
    public Text m_MessageText;
    public GameObject m_TankPrefab;
    public Complete.TankManager[] m_Tanks = new Complete.TankManager[0];
    private int m_RoundNumber = 0;
    private WaitForSeconds m_StartWait;
    private WaitForSeconds m_EndWait;
    private Complete.TankManager m_RoundWinner;
    private Complete.TankManager m_GameWinner;
    private int index;
    private int index1;
    private int index2;
    private int index3;
    private int index4;
    private int index5;

    private void Start() {
      m_StartWait = new WaitForSeconds(m_StartDelay);
      m_EndWait = new WaitForSeconds(m_EndDelay);
      SpawnAllTanks();
      SetCameraTargets();
      base.StartCoroutine(GameLoop());
    }

    private void SpawnAllTanks() {
      for(index = 0; index < m_Tanks.Length; index += 1) {
        m_Tanks[index].m_Instance = (Object.Instantiate<GameObject>(m_TankPrefab, m_Tanks[index].m_SpawnPoint.position, m_Tanks[index].m_SpawnPoint.rotation) as GameObject);
        m_Tanks[index].m_PlayerNumber = (int)(index + 1);
        m_Tanks[index].Setup();
      }
    }

    private void SetCameraTargets() {
      Transform[] targets = null;
      targets = new Transform[m_Tanks.Length];
      for(index1 = 0; index1 < targets.Length; index1 += 1) {
        targets[index1] = m_Tanks[index1].m_Instance.transform;
      }
      m_CameraControl.m_Targets = targets;
    }

    private IEnumerator GameLoop() {
      yield return base.StartCoroutine(RoundStarting());
      yield return base.StartCoroutine(RoundPlaying());
      yield return base.StartCoroutine(RoundEnding());
      if((m_GameWinner != null)) {
        Application.LoadLevel(Application.loadedLevel);
      } else {
        base.StartCoroutine(GameLoop());
      }
    }

    private IEnumerator RoundStarting() {
      ResetAllTanks();
      DisableTankControl();
      m_CameraControl.SetStartPositionAndSize();
      m_RoundNumber += 1;
      m_MessageText.text = ("ROUND " + m_RoundNumber);
      yield return m_StartWait;
    }

    private IEnumerator RoundPlaying() {
      EnableTankControl();
      m_MessageText.text = string.Empty;
      while(!(OneTankLeft())) {
        yield return null;
      }
    }

    private IEnumerator RoundEnding() {
      string message = null;
      DisableTankControl();
      m_RoundWinner = null as Complete.TankManager;
      m_RoundWinner = GetRoundWinner();
      if((m_RoundWinner != null)) {
        m_RoundWinner.m_Wins += 1;
      }
      m_GameWinner = GetGameWinner();
      message = EndMessage();
      m_MessageText.text = message;
      yield return m_EndWait;
    }

    private bool OneTankLeft() {
      int numTanksLeft = 0;
      for(index2 = 0; index2 < m_Tanks.Length; index2 += 1) {
        if(m_Tanks[index2].m_Instance.activeSelf) {
          numTanksLeft += 1;
        }
      }
      return (numTanksLeft <= 1);
    }

    private Complete.TankManager GetRoundWinner() {
      for(index3 = 0; index3 < m_Tanks.Length; index3 += 1) {
        if(m_Tanks[index3].m_Instance.activeSelf) {
          return m_Tanks[index3];
        }
      }
      return null;
    }

    private Complete.TankManager GetGameWinner() {
      for(index4 = 0; index4 < m_Tanks.Length; index4 += 1) {
        if((m_Tanks[index4].m_Wins == m_NumRoundsToWin)) {
          return m_Tanks[index4];
        }
      }
      return null;
    }

    private string EndMessage() {
      string message1 = "DRAW!";
      if((m_RoundWinner != null)) {
        message1 = (m_RoundWinner.m_ColoredPlayerText + " WINS THE ROUND!");
      }
      message1 += "\n\n\n\n";
      for(index5 = 0; index5 < m_Tanks.Length; index5 += 1) {
        message1 += (((m_Tanks[index5].m_ColoredPlayerText + ": ") + m_Tanks[index5].m_Wins) + " WINS\n");
      }
      if((m_GameWinner != null)) {
        message1 = (m_GameWinner.m_ColoredPlayerText + " WINS THE GAME!");
      }
      return message1;
    }

    private void ResetAllTanks() {
      for(int index6 = 0; index6 < m_Tanks.Length; index6 += 1) {
        m_Tanks[index6].Reset();
      }
    }

    private void EnableTankControl() {
      for(int index7 = 0; index7 < m_Tanks.Length; index7 += 1) {
        m_Tanks[index7].EnableControl();
      }
    }

    private void DisableTankControl() {
      for(int index8 = 0; index8 < m_Tanks.Length; index8 += 1) {
        m_Tanks[index8].DisableControl();
      }
    }
  }
}
using System;
using UnityEngine;

namespace Complete {
  [SerializableAttribute]
  public class TankManager : object {
    public Color m_PlayerColor = Color.clear;
    public Transform m_SpawnPoint;
    [HideInInspector]
    public int m_PlayerNumber = 0;
    [HideInInspector]
    public string m_ColoredPlayerText;
    [HideInInspector]
    public GameObject m_Instance;
    [HideInInspector]
    public int m_Wins = 0;
    private Complete.TankMovement m_Movement;
    private Complete.TankShooting m_Shooting;
    private GameObject m_CanvasGameObject;
    private int index;

    public void Setup() {
      MeshRenderer[] renderers = null;
      m_Movement = m_Instance.GetComponent<Complete.TankMovement>();
      m_Shooting = m_Instance.GetComponent<Complete.TankShooting>();
      m_CanvasGameObject = m_Instance.GetComponentInChildren<Canvas>().gameObject;
      m_Movement.m_PlayerNumber = m_PlayerNumber;
      m_Shooting.m_PlayerNumber = m_PlayerNumber;
      m_ColoredPlayerText = (((("<color=#" + ColorUtility.ToHtmlStringRGB(m_PlayerColor)) + ">PLAYER ") + m_PlayerNumber) + "</color>");
      renderers = m_Instance.GetComponentsInChildren<MeshRenderer>();
      for(index = 0; index < renderers.Length; index += 1) {
        renderers[index].material.color = m_PlayerColor;
      }
    }

    public void DisableControl() {
      m_Movement.enabled = false;
      m_Shooting.enabled = false;
      m_CanvasGameObject.SetActive(false);
    }

    public void EnableControl() {
      m_Movement.enabled = true;
      m_Shooting.enabled = true;
      m_CanvasGameObject.SetActive(true);
    }

    public void Reset() {
      m_Instance.transform.position = m_SpawnPoint.position;
      m_Instance.transform.rotation = m_SpawnPoint.rotation;
      m_Instance.SetActive(false);
      m_Instance.SetActive(true);
    }
  }
}
using UnityEngine;

namespace Complete {
  public class ShellExplosion : MonoBehaviour {
    public LayerMask m_TankMask = 0;
    public ParticleSystem m_ExplosionParticles;
    public AudioSource m_ExplosionAudio;
    public float m_MaxDamage = 100F;
    public float m_ExplosionForce = 1000F;
    public float m_MaxLifeTime = 2F;
    public float m_ExplosionRadius = 5F;
    private int index;

    private void Start() {
      Object.Destroy(base.gameObject, m_MaxLifeTime);
    }

    private void OnTriggerEnter(Collider other) {
      Collider[] colliders = null;
      Rigidbody targetRigidbody = null;
      Complete.TankHealth targetHealth = null;
      float damage;
      colliders = Physics.OverlapSphere(base.transform.position, m_ExplosionRadius, m_TankMask);
      for(index = 0; index < colliders.Length; index += 1) {
        targetRigidbody = colliders[index].GetComponent<Rigidbody>();
        if(!(targetRigidbody)) {
          continue;
        }
        targetRigidbody.AddExplosionForce(m_ExplosionForce, base.transform.position, m_ExplosionRadius);
        targetHealth = targetRigidbody.GetComponent<Complete.TankHealth>();
        if(!(targetHealth)) {
          continue;
        }
        damage = CalculateDamage(targetRigidbody.position);
        targetHealth.TakeDamage(damage);
      }
      m_ExplosionParticles.transform.parent = null as Transform;
      m_ExplosionParticles.Play();
      m_ExplosionAudio.Play();
      Object.Destroy(m_ExplosionParticles.gameObject, m_ExplosionParticles.duration);
      Object.Destroy(base.gameObject);
    }

    private float CalculateDamage(Vector3 targetPosition) {
      Vector3 explosionToTarget;
      float explosionDistance;
      float relativeDistance;
      float damage1;
      explosionToTarget = (targetPosition - base.transform.position);
      explosionDistance = explosionToTarget.magnitude;
      relativeDistance = ((m_ExplosionRadius - explosionDistance) / m_ExplosionRadius);
      damage1 = (relativeDistance * m_MaxDamage);
      damage1 = Mathf.Max(0F, damage1);
      return damage1;
    }
  }
}
using UnityEngine;
using UnityEngine.UI;

namespace Complete {
  public class TankHealth : MonoBehaviour {
    public float m_StartingHealth = 100F;
    public Slider m_Slider;
    public UnityEngine.UI.Image m_FillImage;
    public Color m_FullHealthColor = Color.green;
    public Color m_ZeroHealthColor = Color.red;
    public GameObject m_ExplosionPrefab;
    private AudioSource m_ExplosionAudio;
    private ParticleSystem m_ExplosionParticles;
    private float m_CurrentHealth = 0F;
    private bool m_Dead = false;

    private void Awake() {
      m_ExplosionParticles = Object.Instantiate<GameObject>(m_ExplosionPrefab).GetComponent<ParticleSystem>();
      m_ExplosionAudio = m_ExplosionParticles.GetComponent<AudioSource>();
      m_ExplosionParticles.gameObject.SetActive(false);
    }

    private void OnEnable() {
      m_CurrentHealth = m_StartingHealth;
      m_Dead = false;
      SetHealthUI();
    }

    public void TakeDamage(float amount) {
      m_CurrentHealth -= amount;
      SetHealthUI();
      if(((m_CurrentHealth <= 0F) && !(m_Dead))) {
        OnDeath();
      }
    }

    private void SetHealthUI() {
      m_Slider.value = m_CurrentHealth;
      m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, (m_CurrentHealth / m_StartingHealth));
    }

    private void OnDeath() {
      m_Dead = true;
      m_ExplosionParticles.transform.position = base.transform.position;
      m_ExplosionParticles.gameObject.SetActive(true);
      m_ExplosionParticles.Play();
      m_ExplosionAudio.Play();
      base.gameObject.SetActive(false);
    }
  }
}
using UnityEngine;

namespace Complete {
  public class TankMovement : MonoBehaviour {
    public int m_PlayerNumber = 1;
    public float m_Speed = 12F;
    public float m_TurnSpeed = 180F;
    public AudioSource m_MovementAudio;
    public AudioClip m_EngineIdling;
    public AudioClip m_EngineDriving;
    public float m_PitchRange = 0.2F;
    private string m_MovementAxisName;
    private string m_TurnAxisName;
    private Rigidbody m_Rigidbody;
    private float m_MovementInputValue = 0F;
    private float m_TurnInputValue = 0F;
    private float m_OriginalPitch = 0F;

    private void Awake() {
      m_Rigidbody = base.GetComponent<Rigidbody>();
    }

    private void OnEnable() {
      m_Rigidbody.isKinematic = false;
      m_MovementInputValue = 0F;
      m_TurnInputValue = 0F;
    }

    private void OnDisable() {
      m_Rigidbody.isKinematic = true;
    }

    private void Start() {
      m_MovementAxisName = ("Vertical" + m_PlayerNumber);
      m_TurnAxisName = ("Horizontal" + m_PlayerNumber);
      m_OriginalPitch = m_MovementAudio.pitch;
    }

    private void Update() {
      m_MovementInputValue = Input.GetAxis(m_MovementAxisName);
      m_TurnInputValue = Input.GetAxis(m_TurnAxisName);
      EngineAudio();
    }

    private void EngineAudio() {
      if(((Mathf.Abs(m_MovementInputValue) < 0.1F) && (Mathf.Abs(m_TurnInputValue) < 0.1F))) {
        if((m_MovementAudio.clip == m_EngineDriving)) {
          m_MovementAudio.clip = m_EngineIdling;
          m_MovementAudio.pitch = Random.Range((m_OriginalPitch - m_PitchRange), (m_OriginalPitch + m_PitchRange));
          m_MovementAudio.Play();
        }
      } else if((m_MovementAudio.clip == m_EngineIdling)) {
        m_MovementAudio.clip = m_EngineDriving;
        m_MovementAudio.pitch = Random.Range((m_OriginalPitch - m_PitchRange), (m_OriginalPitch + m_PitchRange));
        m_MovementAudio.Play();
      }
    }

    private void FixedUpdate() {
      Move();
      Turn();
    }

    private void Move() {
      Vector3 movement;
      movement = (((base.transform.forward * m_MovementInputValue) * m_Speed) * Time.deltaTime);
      m_Rigidbody.MovePosition((m_Rigidbody.position + movement));
    }

    private void Turn() {
      float turn;
      Quaternion turnRotation;
      turn = ((m_TurnInputValue * m_TurnSpeed) * Time.deltaTime);
      turnRotation = Quaternion.Euler(0F, turn, 0F);
      m_Rigidbody.MoveRotation((m_Rigidbody.rotation * turnRotation));
    }
  }
}
using UnityEngine;
using UnityEngine.UI;

namespace Complete {
  public class TankShooting : MonoBehaviour {
    public int m_PlayerNumber = 1;
    public Rigidbody m_Shell;
    public Transform m_FireTransform;
    public Slider m_AimSlider;
    public AudioSource m_ShootingAudio;
    public AudioClip m_ChargingClip;
    public AudioClip m_FireClip;
    public float m_MinLaunchForce = 15F;
    public float m_MaxLaunchForce = 30F;
    public float m_MaxChargeTime = 0.75F;
    private string m_FireButton;
    private float m_CurrentLaunchForce = 0F;
    private float m_ChargeSpeed = 0F;
    private bool m_Fired = false;

    private void OnEnable() {
      m_CurrentLaunchForce = m_MinLaunchForce;
      m_AimSlider.value = m_MinLaunchForce;
    }

    private void Start() {
      m_FireButton = ("Fire" + m_PlayerNumber);
      m_ChargeSpeed = ((m_MaxLaunchForce - m_MinLaunchForce) / m_MaxChargeTime);
    }

    private void Update() {
      m_AimSlider.value = m_MinLaunchForce;
      if(((m_CurrentLaunchForce >= m_MaxLaunchForce) && !(m_Fired))) {
        m_CurrentLaunchForce = m_MaxLaunchForce;
        Fire();
      } else if(Input.GetButtonDown(m_FireButton)) {
        m_Fired = false;
        m_CurrentLaunchForce = m_MinLaunchForce;
        m_ShootingAudio.clip = m_ChargingClip;
        m_ShootingAudio.Play();
      } else if((Input.GetButton(m_FireButton) && !(m_Fired))) {
        m_CurrentLaunchForce += (m_ChargeSpeed * Time.deltaTime);
        m_AimSlider.value = m_CurrentLaunchForce;
      } else if((Input.GetButtonUp(m_FireButton) && !(m_Fired))) {
        Fire();
      }
    }

    private void Fire() {
      Rigidbody shellInstance = null;
      m_Fired = true;
      shellInstance = (Object.Instantiate<Rigidbody>(m_Shell, m_FireTransform.position, m_FireTransform.rotation) as Rigidbody);
      shellInstance.velocity = (m_CurrentLaunchForce * m_FireTransform.forward);
      m_ShootingAudio.clip = m_FireClip;
      m_ShootingAudio.Play();
      m_CurrentLaunchForce = m_MinLaunchForce;
    }
  }
}
using UnityEngine;

namespace Complete {
  public class UIDirectionControl : MonoBehaviour {
    public bool m_UseRelativeRotation = true;
    private Quaternion m_RelativeRotation = new Quaternion();

    private void Start() {
      m_RelativeRotation = base.transform.parent.localRotation;
    }

    private void Update() {
      if(m_UseRelativeRotation) {
        base.transform.rotation = m_RelativeRotation;
      }
    }
  }
}

 

Graphs preview:

 

 

Requirement

Unity 3D Version 5.6 or higher.
uNode – Visual Scripting Version 1.5.1 or higher, this requirement is optional.

 

Download

Was this helpful to you?

Leave a Reply

Your email address will not be published. Required fields are marked *