3D游戏编程:模型与动画

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/SYSU_yzt/article/details/102754359

1、智能巡逻兵

提交要求:
游戏设计要求:
创建一个地图和若干巡逻兵(使用动画);
每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
巡逻兵在设定范围内感知到玩家,会自动追击玩家;
失去玩家目标后,继续巡逻;
计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
程序设计要求:
必须使用订阅与发布模式传消息
subject:OnLostGoal
Publisher: ?
Subscriber: ?
工厂模式生产巡逻兵
友善提示1:生成 3~5个边的凸多边型
随机生成矩形
在矩形每个边上随机找点,可得到 3 - 4 的凸多边型
5 ?
友善提示2:参考以前博客,给出自己新玩法

游戏演示视频
在这里插入图片描述

游戏设计:

动画部分

我用两个动画控制器来操控角色和怪物
怪物动画控制器:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
怪物中的动画转换用两个布尔类型来操控,当没有碰到角色时,怪物一直处于巡逻动画,当碰到角色时,怪物会触发攻击动画。

角色动画控制器:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
角色动画有三个布尔值来操控,当没走路时,处于静止动画,当走路时处于跑步动画,如果死亡了,则直接进入伤心动画(资源包里没有死亡动画)。

巡逻兵巡逻部分

其实我不是很读的懂题目那个凸多边形的要求。。
我的实现是这样的:
让巡逻兵从上下左右找一个方向来运动,并且运动的距离随机,运动完之后找下一个方向,这样子就能大概实现一个凸多边形的运动轨迹,且这个轨迹是不断在变化的,更具有随机性,下一个目标的位置都是通过方向和距离来相对当前位置得到的。

我的巡逻兵碰到墙会反方向返回(就是找下一个点来运动过去),保证不会走出自己的巡逻范围(各自的房间)。

ps:其实我应该选取房间四边的某点来每次巡逻的,但是我觉得那样子不够随机化,所以我就是以角色自己来进行随机化的选择

巡逻兵脚本如下:

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);
        }
    }
}

工厂模式

工厂单例模式来生产巡逻兵

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()
    {
        
    }
}

消息订阅模式

我用消息订阅模式来实现通知巡逻兵追角色,巡逻兵丢失角色和角色死亡这几个事件。
挂在角色上的脚本有

    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;

巡逻兵会各自订阅这些推送,死亡推送的订阅是model来做的,控制整个系统的运行。
所以我们这个游戏还是MVC模式的。

       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");

我通过判断角色位置来发送推送,如果角色位置切换其实就意味着它摆脱了一个巡逻兵,所以分数加一,死亡推送发生在和巡逻兵碰撞

    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");
        }
    }

代码见github

猜你喜欢

转载自blog.csdn.net/SYSU_yzt/article/details/102754359