Smart Patrol

Smart Patrol

Game requirements:

  • Game Design Requirements

    • Create a map and several patrols.
    • Each patrol soldier walks a convex polygon with 3~5 sides, and the position data is a relative address, that is, each time the next target position is determined, the current position is used as the origin for calculation;
    • When the patrolman collides with an obstacle, it will automatically select the next point target;
    • Patrols will automatically chase the player when they sense the player within the set range;
    • After losing the player target, continue patrolling;
    • Scoring: Players score one point each time they throw off a patrol soldier, and the game ends when the player collides with a patrol soldier.
  • Program design requirements:

    • You must use the subscription and publication mode to pass messages

      • subject:OnLostGoal
      • Publisher:?
      • Subscriber:?
    • factory model production patrol

  • Friendly reminder 1:

    • randomly generated matrix
    • Randomly find points on each side of the rectangle to get 3-4 convex polygons.
    • 5?
  • Friendly reminder 2:

    • Refer to previous blogs and give your own gameplay

Game Prefab:

Download resources from assetstore to get character models and map models.

Map prefabrication :

insert image description here

As shown in the figure, the ground is divided into nine parts, and a BoxCollider component is added to each part, and the is Trigger option is checked, which can be used as a trigger to receive the player's entry event.

insert image description here

At the same time, in order to achieve the isolation effect of the wall (so that the player cannot pass through the wall), it is also necessary to add a mesh collider to each wall. It is not necessary to check the is Trigger option, because there is no additional collision event.

insert image description here

Patrol Prefab:

Patrols need to add a rightbody component in order for the patrols to have a physics engine.
insert image description here

It is also necessary to add a capsule collider component and adjust the size of the capsule to match the size of the character model to detect collision events with the player.

insert image description here
insert image description here

It is also necessary to add the box collider component and check the is Trigger option and set the size of the range to detect whether the player enters the detection range, and if it enters, it will be chased.

insert image description here

Patrol animation prefabrication:

as the picture shows

When the game is not started, the action of the patrol soldier is idle, and the action after the game starts is run. When the player is not within the range, the destination of the patrol soldier's run action is random, and when the player enters the range, it will be Chase the player. And when the game player collides with the patrolling soldier, the patrolling soldier's action will change to attack.

insert image description here

The following describes in detail the method of controlling the movements of the patrolling soldiers.

First add a Boolean variable run and a trigger attack.

When the run variable is false, the action of the patrolling soldier is idle, that is, keep still.

When the run variable is true, the action of the patrolling soldier will change from idle to run.

When the attack trigger is triggered, the patrolling soldier's action will change to attack.

Player character prefabs:

The player character prefabrication is basically the same as the patrolling soldier prefabrication, the only difference is that there is no need to add the box collider component.

Player Character Animation Prefab:

as shown in the picture

insert image description here

This is basically the same as the Patrol prefab, the difference is that the trigger becomes death, and when death is triggered, the player's action becomes die.

Code introduction:

1. mapcollide

Script mounted on each region to detect which region the player is in. Change the identity of the scene controller to the identity of the current area, which can be accessed globally.

public class mapcollide : MonoBehaviour
{
    public int sign = 0;
    FirstSceneController mysceneController;
    private void Start()
    {
        mysceneController = SSDirector.GetInstance().CurrentScenceController as FirstSceneController;
    }
    void OnTriggerEnter(Collider collider)
    {
        if (collider.gameObject.tag == "Player")
        {
            mysceneController.wall_sign = sign;
        }
    }
}

2. playercollide

The script mounted on the player model is used to trigger the attack trigger of the patrol soldier and the death trigger of the player when the player meets the patrol soldier, so that the patrol soldier performs a slashing action and makes the player fall to the ground.

public class playcollide : MonoBehaviour
{

    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.tag == "Player")
        {
            other.gameObject.GetComponent<Animator>().SetTrigger("death");
            this.GetComponent<Animator>().SetTrigger("attack");
            Singleton<GameEventManager>.Instance.PlayerGameover();
        }
    }
}

3. patrolcollide

The script mounted on the patrol soldier model is used to manage whether the patrol soldier chases the player or not. When the player is within the detection range of the patrol soldier, the player will be chased, otherwise it will not be chased.

public class patrolcollide : MonoBehaviour
{
    void OnTriggerEnter(Collider collider)
    {
        if (collider.gameObject.tag == "Player")
        {
            this.gameObject.transform.parent.GetComponent<patrol>().follow_player = true;
            this.gameObject.transform.parent.GetComponent<patrol>().player = collider.gameObject;
        }
    }
    void OnTriggerExit(Collider collider)
    {
        if (collider.gameObject.tag == "Player")
        {
            this.gameObject.transform.parent.GetComponent<patrol>().follow_player = false;
            this.gameObject.transform.parent.GetComponent<patrol>().player = null;
        }
    }
}

4.minecollide

Script mounted on Goldmine. When the player collides with gold mines, make the gold mines disappear and reduce the amount of gold mines.

public class minecollide : MonoBehaviour
{
    void OnTriggerEnter(Collider collider)
    {
        if (collider.gameObject.tag == "Player" && this.gameObject.activeSelf)
        {
            this.gameObject.SetActive(false);
            Singleton<GameEventManager>.Instance.ReduceGoldmineNum();
        }
    }
}

5.patrol

It declares the attributes of the patrolling soldiers. The sign attribute indicates the area number of the patrolling soldier. The follow_player attribute indicates whether to follow the player. The wall_sign indicates the area number of the player. The start_position indicates the initial position of the patrolling soldier.

public class patrol  : MonoBehaviour
{
    public int sign;                      
    public bool follow_player = false;    
    public int wall_sign = -1;            
    public GameObject player;             
    public Vector3 start_position;            
}

6.myfactory

A factory that produces gold mines and patrol soldiers. Nine patrol soldiers are created and their initial positions are given. Twelve gold mines are created and the initial positions are randomly produced.

public class myfactory : MonoBehaviour
{
    private GameObject patrol = null;                              
    private List<GameObject> usedPatrol = new List<GameObject>();        
    private GameObject goldmine = null;                             
    private List<GameObject> usedgoldmine = new List<GameObject>();      


    public List<GameObject> GetPatrols()
    {
        int[] pos_x = { -6, 4, 13 };
        int[] pos_z = { -4, 6, -13 };
        int index = 0;
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                Vector3 vec = new Vector3(pos_x[i], 0, pos_z[j]);
                patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));
                patrol.transform.position = vec;
                patrol.GetComponent<patrol>().sign = index + 1;
                patrol.GetComponent<patrol>().start_position = vec;
                usedPatrol.Add(patrol);
                index++;
            }
        }

        return usedPatrol;
    }


    public List<GameObject> GetGoldmine()
    {
        float range = 12;                                     
        for (int i = 0; i < 12; i++)
        {
            goldmine = Instantiate(Resources.Load<GameObject>("Prefabs/Goldmine"));
            float ranx = Random.Range(-range, range);
            float ranz = Random.Range(-range, range);
            goldmine.transform.position = new Vector3(ranx, 0, ranz);
            usedgoldmine.Add(goldmine);
        }

        return usedgoldmine;
    }
    
    public void StopPatrol()
    {
        int size = usedPatrol.Count;
        for (int i = 0; i < size; i++)
        {
            usedPatrol[i].gameObject.GetComponent<Animator>().SetBool("run", false);
        }
    }
}

7.SSActionManager

An actiondic of Dictionary type is defined, which represents a list of execution actions. Two addlist and deletelist of List type are defined, respectively representing the action queue waiting to be executed and the action queue waiting to be deleted. Each update will add the actions in addlist to the execution action list, and delete the actions in deletelist from the execution action list.

public class SSActionManager : MonoBehaviour, ISSActionCallback
{
    private Dictionary<int, SSAction> actiondic = new Dictionary<int, SSAction>();    
    private List<SSAction> addlist = new List<SSAction>();                       
    private List<int> deletelist = new List<int>();                                            

    protected void Update()
    {
        foreach (SSAction myssaction in addlist)
        {
            actiondic[myssaction.GetInstanceID()] = myssaction;
        }
        addlist.Clear();
        foreach (KeyValuePair<int, SSAction> kv in actiondic)
        {
            SSAction myssaction = kv.Value;
            if (myssaction.destroy)
            {
                deletelist.Add(myssaction.GetInstanceID());
            }
            else if (myssaction.enable)
            {
                myssaction.Update();
            }
        }
        foreach (int key in deletelist)
        {
            SSAction myssaction = actiondic[key];
            actiondic.Remove(key);
            DestroyObject(myssaction);
        }
        deletelist.Clear();
    }
 
    public void RunAction(GameObject gameobject, SSAction ssaction, ISSActionCallback manager)
    {
        ssaction.gameobject = gameobject;
        ssaction.transform = gameobject.transform;
        ssaction.callback = manager;
        addlist.Add(ssaction);
        ssaction.Start();
    }

    public void SSActionEvent(SSAction source, int intParam = 0, GameObject objectParam = null)
    {
        if(intParam == 0)
        {
            patrolchaseaction follow = patrolchaseaction.GetSSAction(objectParam.gameObject.GetComponent<patrol>().player);
            this.RunAction(objectParam, follow, this);
        }
        else
        {
            Patrolaction move = Patrolaction.GetSSAction(objectParam.gameObject.GetComponent<patrol>().start_position);
            this.RunAction(objectParam, move, this);
            Singleton<GameEventManager>.Instance.PlayerEscape();
        }
    }

	
    public void DestroyAll()
    {
        foreach (KeyValuePair<int, SSAction> kv in actiondic)
        {
            SSAction myssaction = kv.Value;
            myssaction.destroy = true;
        }
    }
}

8.Patrolaction

The Patrol action class specifies the actions of the patrol, mainly defining the action of patrolling when the patrol is not chasing the player and the action of chasing the player.

public class GameEventManager : MonoBehaviour
{
    public delegate void ScoreEvent();
    public static event ScoreEvent ScoreChange;
    public delegate void GameoverEvent();
    public static event GameoverEvent GameoverChange;
    public delegate void GoldmineEvent();
    public static event GoldmineEvent GoldmineChange;

    public void PlayerEscape()
    {
        if (ScoreChange != null)
        {
            ScoreChange();
        }
    }
    public void PlayerGameover()
    {
        if (GameoverChange != null)
        {
            GameoverChange();
        }
    }
    public void ReduceGoldmineNum()
    {
        if (GoldmineChange != null)
        {
            GoldmineChange();
        }
    }
}

9.Patrolactionmanager

A management class that manages patrol actions.

public class Patrolactionmanager : SSActionManager
{
    private Patrolaction patrolact;

    public void GoPatrol(GameObject patrol)
    {
        patrolact = Patrolaction.GetSSAction(patrol.transform.position);
        this.RunAction(patrol, patrolact, this);
    }
    public void DestroyAllAction()
    {
        DestroyAll();
    }
}

10.gameeventmanager

The publisher in the subscription and publishing mode manages three actions, which are the bonus points after the player escapes the hunt, the game ends when the player gets all the gold mines or is caught, and the number of gold mines decreases after the player touches the gold mines.

public class GameEventManager : MonoBehaviour
{
    public delegate void ScoreEvent();
    public static event ScoreEvent ScoreChange;
    public delegate void GameoverEvent();
    public static event GameoverEvent GameoverChange;
    public delegate void GoldmineEvent();
    public static event GoldmineEvent GoldmineChange;

    public void PlayerEscape()
    {
        if (ScoreChange != null)
        {
            ScoreChange();
        }
    }
    public void PlayerGameover()
    {
        if (GameoverChange != null)
        {
            GameoverChange();
        }
    }
    public void ReduceGoldmineNum()
    {
        if (GoldmineChange != null)
        {
            GoldmineChange();
        }
    }
}

11.FirstSceneController

The most important controller in this project is also the subscriber in the subscription and release mode. It subscribes to the events of Gameeventmanager. If the subscribed event occurs, it will cause the scene controller to call the registered method. At the same time, there are functions such as loading resources and updating game state.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class FirstSceneController : MonoBehaviour, IUserAction, ISceneController
{
    public myfactory patrol_factory;                               
    public graderecorder recorder;                         
    public Patrolactionmanager action_manager;                  
    public int wall_sign = -1;                                     
    public GameObject player;                                      
    public Camera main_camera;                                    
    public float player_speed = 5;                                  
    public float rotate_speed = 250f;                               
    private List<GameObject> patrols;                                
    private List<GameObject> goldmines;                               
    private bool game_over = false;                                 

    void Update()
    {
        for (int i = 0; i < patrols.Count; i++)
        {
            patrols[i].gameObject.GetComponent<patrol>().wall_sign = wall_sign;
        }
        if(recorder.GetGoldmineNumber() == 0)
        {
            Gameover();
        }
    }
    void Start()
    {
        SSDirector director = SSDirector.GetInstance();
        director.CurrentScenceController = this;
        patrol_factory = Singleton<myfactory>.Instance;
        action_manager = gameObject.AddComponent<Patrolactionmanager>() as Patrolactionmanager;
        LoadResources();
        main_camera.GetComponent<camera>().follow = player;
        recorder = Singleton<graderecorder>.Instance;
    }

    public void LoadResources()
    {
        Instantiate(Resources.Load<GameObject>("Prefabs/Plane"));
        player = Instantiate(Resources.Load("Prefabs/Player"), new Vector3(0, 9, 0), Quaternion.identity) as GameObject;
        goldmines = patrol_factory.GetGoldmine();
        patrols = patrol_factory.GetPatrols();
        for (int i = 0; i < patrols.Count; i++)
        {
            action_manager.GoPatrol(patrols[i]);
        }
    }
    public void MovePlayer(float translationX, float translationZ)
    {
        if(!game_over)
        {
            if (translationX != 0 || translationZ != 0)
            {
                player.GetComponent<Animator>().SetBool("run", true);
            }
            else
            {
                player.GetComponent<Animator>().SetBool("run", false);
            }
            player.transform.Translate(0, 0, translationZ * player_speed * Time.deltaTime);
            player.transform.Rotate(0, translationX * rotate_speed * Time.deltaTime, 0);
            if (player.transform.localEulerAngles.x != 0 || player.transform.localEulerAngles.z != 0)
            {
                player.transform.localEulerAngles = new Vector3(0, player.transform.localEulerAngles.y, 0);
            }
            if (player.transform.position.y != 0)
            {
                player.transform.position = new Vector3(player.transform.position.x, 0, player.transform.position.z);
            }     
        }
    }

    public int GetScore()
    {
        return recorder.GetScore();
    }

    public int GetGoldmineNumber()
    {
        return recorder.GetGoldmineNumber();
    }
    public bool GetGameover()
    {
        return game_over;
    }
    public void Restart()
    {
        SceneManager.LoadScene("Scenes/mySence");
    }

    void OnEnable()
    {
        GameEventManager.ScoreChange += AddScore;
        GameEventManager.GameoverChange += Gameover;
        GameEventManager.GoldmineChange += ReduceGoldmineNumber;
    }
    void OnDisable()
    {
        GameEventManager.ScoreChange -= AddScore;
        GameEventManager.GameoverChange -= Gameover;
        GameEventManager.GoldmineChange -= ReduceGoldmineNumber;
    }
    void ReduceGoldmineNumber()
    {
        recorder.ReduceGoldmine();
    }
    void AddScore()
    {
        recorder.AddScore();
    }
    void Gameover()
    {
        game_over = true;
        patrol_factory.StopPatrol();
        action_manager.DestroyAllAction();
    }
}

Project address and demonstration video

Project address
Open myscene in Scenes and run it.

demo video

Guess you like

Origin blog.csdn.net/weixin_52801288/article/details/128358202