3D game programming: model and animation

1. Intelligent patrol

Submission requirements:
Game design requirements:
Create a map and several patrols (using animation);
Each patrol takes a convex multilateral form with 3-5 sides, and the location data is the relative address. That is to say, each time the next target position is determined, it is calculated with its current position as the origin;
When the patrolman collides with an obstacle, he will automatically select the next point as the target.
When the patrolman senses the player within the set range, he will automatically chase the player.
After losing the player's target, continue to patrol;
Score: players lose one patrol every time, and the game is over;
Program design requirements:
You must use subscription and publish mode to deliver messages
subject: OnLostGoal
Publisher: ?
Subscriber: ?
Factory mode production patrol
Friendly tip 1: generate 3-5 convex polyhedrons
Randomly generated rectangle
By randomly finding points on each edge of rectangle, we can get 3 - 4 convex polyhedron
5 ?
Friendly tip 2: refer to previous blogs and give new ways to play

Game demo video

Game design:

Animation part

I use two animation controllers to control characters and monsters.
Monster animation controller:



The animation conversion in monsters is controlled by two Boolean types. When the characters are not touched, the monsters are always in patrol animation. When the characters are touched, the monsters will trigger attack animation.

Character animation controller:




The character animation has three Boolean values to control. When it is not walking, it is in the static animation. When it is walking, it is in the running animation. If it is dead, it will directly enter the sad animation (there is no death animation in the resource package).

Patrol part

In fact, I don't really understand the requirements of convex polygon.
My implementation is as follows:
Let the patrolman find a direction to move from up to down, left and right, and the distance of the movement is random. After the movement, find the next direction. In this way, you can roughly realize the movement track of a convex polygon, which is constantly changing and more random. The position of the next target is obtained by the direction and distance relative to the current position.

My patrolmen will return to the opposite direction when they touch the wall (i.e. find the next point to move past), and make sure they don't go out of their patrol area (their respective rooms).

ps: actually, I should choose a point on the four sides of the room to patrol every time, but I don't think that's enough randomization, so I just choose the randomization by the character himself.

The patrol script is as follows:

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


public class XLBMind : MonoBehaviour
{
    public enum State : int { IDLE, LEFT, UP, RIGHT, DOWN }
    // Start is called before the first frame update
    public Vector3 target;
    public Animator ani;
    public State mystate;
    public bool locked;
    public int cd;

    void OnEnable(){
        if(this.transform.name == "xlb1"){
            Me.InArea1 += pursue;
        }
        if(this.transform.name == "xlb2"){
            Me.InArea2 += pursue;
        }
        if(this.transform.name == "xlb3"){
            Me.InArea3 += pursue;
        }
        if(this.transform.name == "xlb4"){
            Me.InArea4 += pursue;
        }
    }

    void Disable(){

    }

    void pursue(GameObject self,string info){
        target=self.transform.position;
    }

    void Start()
    {
        locked=false;
        cd=0;
        ani = GetComponent<Animator>();
        ani.SetBool("Walking", true);
        ani.SetBool("Attack", false);
        Vector3 del = new Vector3(-1,0,0) * Random.Range(1, 5f);
        mystate=State.RIGHT;
        target = this.transform.position - del;
        OnEnable();
        // Quaternion rotation = Quaternion.LookRotation(target - transform.position);
        // transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * 5f * 5);
        // this.transform.position = Vector3.MoveTowards(this.transform.position, target, 2f * Time.deltaTime);  

    }

    void FixedUpdate(){
        cd=cd+1;
        //Debug.Log(cd);
    }

    // Update is called once per frame
    void Update()
    {
        if(Controller.getInstance().running == false){ 
            return;
        }
        if(locked) return;

        if(this.transform.name == "xlb1" && (target.x<0 || target.z>8)){
            changeDirection2();
        }
        if(this.transform.name == "xlb2" && (target.x>-1 || target.z>8)){
            changeDirection2();
        }
        if(this.transform.name == "xlb3" && (target.x>-1 || target.z<9)){
            changeDirection2();
        }
        if(this.transform.name == "xlb4" && (target.x<0 || target.z<9)){
            changeDirection2();
        }

        Quaternion rotation = Quaternion.LookRotation(target - transform.position);
        transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * 5f * 5);
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, 1f * Time.deltaTime);  
        if(this.transform.position == target){
            changeDirection2();
            //ani.SetBool("Walking", false);
        } 
    }

    private void changeDirection1(){
        locked=true;
        if(mystate == State.LEFT){
            Vector3 del = new Vector3(-1,0,0) * Random.Range(1, 5f);
            mystate=State.RIGHT;
            target = this.transform.position - del;
        } else if(mystate == State.RIGHT){
            Vector3 del = new Vector3(1,0,0) * Random.Range(1, 5f);
            mystate=State.LEFT;
            target = this.transform.position - del;
        } else if(mystate == State.UP){
            Vector3 del = new Vector3(0,0,1) * Random.Range(1, 5f);
            mystate=State.DOWN;
            target = this.transform.position - del;
        } else if(mystate == State.DOWN){
            Vector3 del = new Vector3(0,0,-1) * Random.Range(1, 5f);
            mystate=State.UP;
            target = this.transform.position - del;
        }
        locked=false;
    }

    private void changeDirection2(){
        locked=true;
        if(mystate == State.LEFT){
            Vector3 del = new Vector3(0,0,1) * Random.Range(1, 5f);
            mystate=State.DOWN;
            target = this.transform.position - del;
        } else if(mystate == State.RIGHT){
            Vector3 del = new Vector3(0,0,-1) * Random.Range(1, 5f);
            mystate=State.UP;
            target = this.transform.position - del;
        } else if(mystate == State.UP){
            Vector3 del = new Vector3(-1,0,0) * Random.Range(1, 5f);
            mystate=State.RIGHT;
            target = this.transform.position - del;
        } else if(mystate == State.DOWN){
            Vector3 del = new Vector3(1,0,0) * Random.Range(1, 5f);
            mystate=State.LEFT;
            target = this.transform.position - del;;
        }
        locked=false;
    }

    private void OnTriggerEnter(Collider other)
    {
        string name = other.gameObject.name;
        if(other.gameObject.name == "Cube") {
            if(cd < 60) return;
            cd=0;
            Debug.Log(this.transform.gameObject.name+"change");
            changeDirection1();
        }
        else
        {
            //Debug.Log(other.gameObject.name);
            ani.SetBool("Attack", true);
        }
    }
}

Factory mode

Factory singleton mode to produce patrolmen

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

public class Sing : MonoBehaviour
{
    protected static Factory instance;

    public static Factory Instance{
        get{
            if(instance == null){
                instance = (Factory)FindObjectOfType(typeof(Factory));
            }
        return instance;
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

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

public class Factory : MonoBehaviour
{


    // Start is called before the first frame update
    void Start()
    {
        Vector3 pos1=new Vector3(7,0,5);
        Vector3 pos2=new Vector3(-4,0,5);
        Vector3 pos3=new Vector3(-4,0,14);
        Vector3 pos4=new Vector3(7,0,14);
        GameObject xlb1;
        GameObject xlb2;
        GameObject xlb3;
        GameObject xlb4;
        xlb1 = Instantiate(Resources.Load("Prefabs/XLB"), pos1, Quaternion.identity) as GameObject; 
        xlb2 = Instantiate(Resources.Load("Prefabs/XLB"), pos2, Quaternion.identity) as GameObject;
        xlb3 = Instantiate(Resources.Load("Prefabs/XLB"), pos3, Quaternion.identity) as GameObject;
        xlb4 = Instantiate(Resources.Load("Prefabs/XLB"), pos4, Quaternion.identity) as GameObject; 
        xlb1.transform.gameObject.name = "xlb1"; 
        xlb2.transform.gameObject.name = "xlb2"; 
        xlb3.transform.gameObject.name = "xlb3"; 
        xlb4.transform.gameObject.name = "xlb4"; 
    }

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

Message subscription mode

I use the message subscription mode to notify the patrolmen to pursue the role, and the patrolmen lose the role and die.
Scripts that hang on roles are

    public delegate void Myposition(GameObject sender, string info);
    public static event Myposition InArea1;
    public static event Myposition InArea2;
    public static event Myposition InArea3;
    public static event Myposition InArea4;
    public static event Myposition addscore;
    public static event Myposition die;

Patrolmen will subscribe to these push separately. The subscription of death push is done by model, which controls the operation of the whole system.
So our game is still in MVC mode.

       int t=test;
        if(transform.position.x>0 && transform.position.z<9){
            test=1;
            InArea1(this.gameObject,"area1");
        }
        if(transform.position.x<-1 && transform.position.z<9){
            test=2;
            InArea2(this.gameObject,"area2");
        }
        if(transform.position.x<-1 && transform.position.z>10){
            test=3;
            InArea3(this.gameObject,"area3");
        }
        if(transform.position.x>0 && transform.position.z>10){
            test=4;
            InArea4(this.gameObject,"area4");
        }
        if(t!=test) addscore(this.gameObject,"addscore");

I send push by judging the role position. If the role position is switched, it means that it gets rid of a patrolman, so the score plus one, death push happens when it collides with patrolman.

    private void OnTriggerEnter(Collider other) {
        Debug.Log(other.gameObject.tag);
        if(other.gameObject.tag=="Finish"){
            die(this.gameObject,"die");
            ani.SetBool("die",true);
            Debug.Log("die");
        }
    }

Code see github

Keywords: github

Added by dhrosti on Sat, 26 Oct 2019 07:20:32 +0300