3D游戏设计作业9:游戏智能

2023-11-05

坦克对战游戏 AI 设计

游戏截图:
在这里插入图片描述
1,作业要求

从商店下载游戏:“Kawaii” Tank 或 其他坦克模型,构建 AI 对战坦克。具体要求

  • 使用“感知-思考-行为”模型,建模 AI 坦克
  • 场景中要放置一些障碍阻挡对手视线
  • 坦克需要放置一个矩阵包围盒触发器,以保证 AI坦克能使用射线探测对手方位
  • AI 坦克必须在有目标条件下使用导航,并能绕过障碍。(失去目标时策略自己思考)
  • 实现人机对战

2,设计思路
主要分为7个步骤
1,坦克基类Tank.cs:血量,以及被攻击后血量改变的相关函数,还有开炮对应的操作
2,玩家类的实现:继承Tank类
3,AI坦克类的实现,也是要继承Tank类
4,子弹bullet类,有些要求是要注意的:设置发射子弹的坦克类型, 因为如果射到队友是不能算伤害的;如果是玩家发出的子弹伤害高一点,AI坦克的子弹伤害低一点
5,接着就是工厂类了,工厂类无非就是场景要“演员”的时候就生产出来,不需要的时候就回收
6,场景类:获得工厂实例,做好初始化工作,接受从UI类里传过来的要求实现对应的操作
7,最后就是UI类和导演Director类了,UI类就是检测玩家的输入,再对player坦克进行对应的操作

3,关键代码

坦克基类Tank:

public class Tank : MonoBehaviour {
     private float hp =500.0f;
    // 初始化
     public Tank()
     {
         hp = 500.0f;
     }

     public float getHP()
     {
         return hp;
     }

     public void setHP(float hp)
     {
         this.hp = hp;
     }
     //标明子弹所属的坦克类型,TankType是枚举变量,在工厂类里MyFactory定义。
     public void shoot(TankType type)
     {
         GameObject bullet = Singleton<MyFactory>.Instance.getBullets(type);
         bullet.transform.position = new Vector3(transform.position.x, 1.5f, transform.position.z) + transform.forward * 1.5f;
         bullet.transform.forward = transform.forward; //方向
         bullet.GetComponent<Rigidbody>().AddForce(bullet.transform.forward * 20, ForceMode.Impulse);
     }
}

玩家类Player:

public class Player : Tank{
   // player被摧毁时发布信息;
   public delegate void DestroyPlayer();
   public static event DestroyPlayer destroyEvent;
   void Start () {
       setHP(500);
}

// Update is called once per frame
void Update () {
    if(getHP() <= 0)    // Tank is destoryed
       {
           this.gameObject.SetActive(false);
           destroyEvent();
       }
}

   //向前移动
   public void moveForward()
   {
       gameObject.GetComponent<Rigidbody>().velocity = gameObject.transform.forward * 30;
   }
   //向后移动
   public void moveBackWard()
   {
       gameObject.GetComponent<Rigidbody>().velocity = gameObject.transform.forward * -30;
   }

   //通过水平轴上的增量,改变玩家坦克的欧拉角,从而实现坦克转向
   public void turn(float offsetX)
   {
       float x = gameObject.transform.localEulerAngles.x;
       float y = gameObject.transform.localEulerAngles.y + offsetX*2;
       gameObject.transform.localEulerAngles = new Vector3(x, y, 0);
   }
}

AI坦克类Enemy:

public class Enemy : Tank {
   public delegate void RecycleEnemy(GameObject enemy);
   //当enemy被摧毁时,通知工厂回收;
   public static event RecycleEnemy recycleEnemy;
   // player 的位置
   private Vector3 playerLocation;
   //游戏是否结束
   private bool gameover;
   private void Start()
   {
       playerLocation = GameDirector.getInstance().currentSceneController.getPlayer().transform.position;
       StartCoroutine(shoot());
   }

   void Update() {
       playerLocation = GameDirector.getInstance().currentSceneController.getPlayer().transform.position;
       gameover = GameDirector.getInstance().currentSceneController.getGameOver();
       if (!gameover)
       {
           if (getHP() <= 0 && recycleEnemy != null)
           {
               recycleEnemy(this.gameObject);
           }
           else
           {
               // 自动向player移动
               NavMeshAgent agent = gameObject.GetComponent<NavMeshAgent>();
               agent.SetDestination(playerLocation);
              // gameObject.transform.position = new Vector3(transform.position.x+0.01f,transform.position.y,transform.position.z);
           }
       }
       else
       {
           //游戏结束,停止寻路
           NavMeshAgent agent = gameObject.GetComponent<NavMeshAgent>();
           agent.velocity = Vector3.zero;
           agent.ResetPath();
       }
   }
   // 协程实现每隔1s进行射击,开始喜欢协程了
   IEnumerator shoot()
   {
       while (!gameover)
       {
           for(float i =1;i> 0; i -= Time.deltaTime)
           {
               yield return 0;
           }
           if(Vector3.Distance(playerLocation,gameObject.transform.position) < 14)
           {
               shoot(TankType.ENEMY);
           }
       }
   }
}

子弹类Bullet:

public class Bullet : MonoBehaviour {
    public float explosionRadius = 3.0f;
    private TankType tankType;

    //设置发射子弹的坦克类型, 因为如果射到队友是不能算伤害的.
    public void setTankType(TankType type)
    {
        tankType = type;
    }
    private void OnCollisionEnter(Collision collision)
    {
        if(collision.transform.gameObject.tag == "tankEnemy" && this.tankType == TankType.ENEMY ||
            collision.transform.gameObject.tag == "tankPlayer" && this.tankType == TankType.PLAYER)
        {
            return;
        }
        MyFactory factory = Singleton<MyFactory>.Instance;
        ParticleSystem explosion = factory.getParticleSystem();
        explosion.transform.position = gameObject.transform.position;
        //获取爆炸范围内的所有碰撞体
        Collider[] colliders = Physics.OverlapSphere(gameObject.transform.position, explosionRadius);

        foreach(var collider in colliders)
        {
            //被击中坦克与爆炸中心的距离
            float distance = Vector3.Distance(collider.transform.position, gameObject.transform.position);
            float hurt;
            // 如果是玩家发出的子弹伤害高一点
            if (collider.tag == "tankEnemy" && this.tankType == TankType.PLAYER)
            {
                hurt = 300.0f / distance;
                collider.GetComponent<Tank>().setHP(collider.GetComponent<Tank>().getHP() - hurt);
            }
            else if(collider.tag == "tankPlayer" && this.tankType == TankType.ENEMY)
            {
                hurt = 100.0f / distance;
                collider.GetComponent<Tank>().setHP(collider.GetComponent<Tank>().getHP() - hurt);
            }
            explosion.Play();
        }

        if (gameObject.activeSelf)
        {
            factory.recycleBullet(gameObject);
        }
    }

}

工厂类MyFactory:

//其中public的四个变量是用来在unity中将player,enemy,bullet,explosion预制拖进代码里用于实例化对象的。
//其它变量和方法应该看名称都可以看懂。
public enum TankType { PLAYER , ENEMY};
public class MyFactory : MonoBehaviour {

   public GameObject player;
   public GameObject enemy;
   public GameObject bullet;
   public ParticleSystem explosion;

   private List<GameObject> usingTanks;
   private List<GameObject> freeTanks;
   private List<GameObject> usingBullets;
   private List<GameObject> freeBullets;
   private GameObject role;
   private List<ParticleSystem> particles;

   private void Awake()
   {
       usingTanks = new List<GameObject>();
       freeTanks = new List<GameObject>();
       usingBullets = new List<GameObject>();
       freeBullets = new List<GameObject>();
       particles = new List<ParticleSystem>();

       role = GameObject.Instantiate<GameObject>(player) as GameObject;
       role.SetActive(true);
       role.transform.position = Vector3.zero;
   }
   // Use this for initialization
   void Start () {
       Enemy.recycleEnemy += recycleEnemy;
   }

// Update is called once per frame
public GameObject getPlayer()
   {      
       return role;
   }

   public GameObject getEnemys()
   {
       GameObject newTank = null;
       if (freeTanks.Count <= 0)
       {
           newTank = GameObject.Instantiate<GameObject>(enemy) as GameObject;
           usingTanks.Add(newTank);
           newTank.transform.position = new Vector3(Random.Range(-100, 100), 0, Random.Range(-100, 100));
       }
       else
       {
           newTank = freeTanks[0];
           freeTanks.RemoveAt(0);
           usingTanks.Add(newTank);
       }
       newTank.SetActive(true);
       return newTank;
   }

   public GameObject getBullets(TankType type)
   {
       GameObject newBullet;
       if(freeBullets.Count <= 0)
       {
           newBullet = GameObject.Instantiate<GameObject>(bullet) as GameObject;
           usingBullets.Add(newBullet);
           newBullet.transform.position = new Vector3(Random.Range(-100, 100), 0, Random.Range(-100, 100));
       }
       else
       {
           newBullet = freeBullets[0];
           freeBullets.RemoveAt(0);
           usingBullets.Add(newBullet);
       }
       newBullet.GetComponent<Bullet>().setTankType(type);
       newBullet.SetActive(true);
       return newBullet;
   }

   public ParticleSystem getParticleSystem()
   {
       foreach(var particle in particles)
       {
           if (!particle.isPlaying)
           {
               return particle;
           }
       }
       ParticleSystem newPS = GameObject.Instantiate<ParticleSystem>(explosion);
       particles.Add(newPS);
       return newPS;
   }

   public void recycleEnemy(GameObject enemyTank)
   {
       usingTanks.Remove(enemyTank);
       freeTanks.Add(enemyTank);
       enemyTank.GetComponent<Rigidbody>().velocity = Vector3.zero;
       enemyTank.SetActive(false);
   }

   public void recycleBullet(GameObject Bullet)
   {
       usingBullets.Remove(Bullet);
       freeBullets.Add(Bullet);
       Bullet.GetComponent<Rigidbody>().velocity = Vector3.zero;
       Bullet.SetActive(false);
   }
}

场景类SceneController:

// SceneController.cs
public class SceneController : MonoBehaviour,IUserAction{
    public GameObject player;
    private int enemyCount = 6;
    private bool gameOver = false;
    private GameObject[] enemys;
    private MyFactory myFactory;
    public GameDirector director;
    private void Awake()
    {
        director = GameDirector.getInstance();
        director.currentSceneController = this;
        enemys = new GameObject[enemyCount];
        gameOver = false;
        myFactory = Singleton<MyFactory>.Instance;

    }

    void Start () {
        player = myFactory.getPlayer();
        for (int i = 0; i < enemyCount; i++)
        {
            enemys[i]=myFactory.getEnemys();
        }
        Player.destroyEvent += setGameOver;
    }

    // Update is called once per frame
    void Update () {
        Camera.main.transform.position = new Vector3(player.transform.position.x, 18, player.transform.position.z);
    }

    //返回玩家坦克的位置
    public GameObject getPlayer()
    {
        return player;
    }

    //返回游戏状态
    public bool getGameOver()
    {
        return gameOver;
    }

    //设置游戏结束
    public void setGameOver()
    {
        gameOver = true;
    }

    public void moveForward()
    {
        player.GetComponent<Player>().moveForward();
    }
    public void moveBackWard()
    {
        player.GetComponent<Player>().moveBackWard();
    }

    //通过水平轴上的增量,改变玩家坦克的欧拉角,从而实现坦克转向
    public void turn(float offsetX)
    {
        player.GetComponent<Player>().turn(offsetX);
    }

    public void shoot()
    {
        player.GetComponent<Player>().shoot(TankType.PLAYER);
    }
}

//IUserAction.cs
public interface IUserAction
{
    void moveForward();
    void moveBackWard();
    void turn(float offsetX);
    void shoot();
    bool getGameOver();
}

UI类IUserGUI:

//IUserGUI.cs
public class IUserGUI : MonoBehaviour {
  IUserAction action;

    // Use this for initialization
    void Start () {
       action = GameDirector.getInstance().currentSceneController as IUserAction;
    }

// Update is called once per frame
    void Update () {
     if (!action.getGameOver())
     {
         if (Input.GetKey(KeyCode.W))
         {
             action.moveForward();
         }

         if (Input.GetKey(KeyCode.S))
         {
             action.moveBackWard();
         }


         if (Input.GetKeyDown(KeyCode.Space))
         {
             action.shoot();
         }
         //获取水平轴上的增量,目的在于控制玩家坦克的转向
         float offsetX = Input.GetAxis("Horizontal");
         action.turn(offsetX);
     }
}

 void OnGUI()
 {
     //gameover时生成提示
     if (action.getGameOver())
     {
         GUIStyle fontStyle = new GUIStyle();
         fontStyle.fontSize = 30;
         fontStyle.normal.textColor = new Color(0, 0, 0);
         GUI.Button(new Rect(Screen.width/2-50, Screen.height/2-50, 200, 50), "GameOver!");
     }
   }
}

导演Director类GameDirector:

//GameDirector.cs
public class GameDirector : System.Object {
   private static GameDirector _instance;
   public SceneController currentSceneController { get; set; }

   private GameDirector() { }
   public static GameDirector getInstance()
   {
       if(_instance == null)
       {
           _instance = new GameDirector();
       }
       return _instance;
   }
}

4,实验总结
其他部分和之前的作业基本是一样的,重点是增加了电脑敌人的AI设计。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

3D游戏设计作业9:游戏智能 的相关文章

  • 深度学习3D可视化工具——Zetane Engine

    神经网络在工作的时候 里面到底是什么样 为了能透视这个 AI黑箱 中的过程 加拿大蒙特利尔一家公司开发一个3D可视化工具Zetane Engine 只需要上传一个模型 Zetane Engine就可以巡视整个神经网络 并且还可以放大网络中的
  • React-Router实战:路由传参(正则表达式)

    首先我们先做个路由普通传参的例子 一 准备工作 1 目录结构 index js components App gt App js Home gt Home js About gt About js News gt News js 2 源码
  • Qt常用命令和pro参数

    Qt常用工具 命令行指令 位于 C Qt5 7 1 5 7 msvc2015 64 bin 命令 功能 assistant 帮助文档 designer 设计器 linguist 翻译工具 lupdate 提取翻译字符串和生成ts文件 lre
  • logback不输出至文件_Logback日志使用详解

    Logback是由log4j创始人设计的一个开源日志组件 概述 Logback建立于三个主要类之上 日志记录器 Logger 输出端 Appender 和日志格式化器 Layout 这三种组件协同工作 使开发者可以按照消息类型和级别来记录消
  • caffe 红绿灯识别

    coding utf 8 加载必要的库 import numpy as np import sys os 设置当前目录 caffe root home ubuntu caffe sys path insert 0 caffe root py
  • Report, 20150402, Formulas on Entropy, Part I

    Section 1 Papoulis s Formula Lemma 1 If the random variables y 1 ldots y n are the linear combination of random variable
  • Artifactory Maven 使用教程

    Maven 仓库使用 修改 Maven 配置文件 选择左侧 Artifacts 选择自己需要上传的 Maven 仓库 点击Set Me Up 在弹出的设置框中点击 点击下载生成的文件 将生成的文件放到此目录下 或者你自己的 Maven 目录
  • 线性回归模型

    线性回归是最简单的机器学习模型 也是最基础最重要的分析工具 易于实现 本文将将简单讲述线性回归 最小二乘法和梯度下降三种算法 目录 1 线性回归方程 OLS 2 最小二乘法 OLS 3 梯度下降 GD 3 1超参数 的选择 3 2局部最小值
  • Android使用OpenCV来实现bitmap独立设置每个圆角

    Android使用OpenCV来实现bitmap独立设置每个圆角 关于opencv集成请参考我的其他文章 这里方便起见已经封装成java方法供大家调用 代码如下 public static Bitmap drawCircleRadius i
  • 打乱1-100的个数字的顺序

    package test import java util Random public class Test2 public static void swap int a int i int j if a null a length 0 i
  • Weex页面的编写——Weex的学习之路(六)

    通过前几博客我们把weex的内置组件都学习完了 组件的单独使用想必都可以了 那么 这次我们来做weex实际页面的编写 见证一下 一套代码在Android Ios和H5上使用 在weex官网推荐我们使用Weex Studio作为编译器 其实这
  • Unity 3D作业二:离散仿真引擎基础

    前言 中山大学中山大学数据科学与计算机学院3D游戏课程学习记录博客 简答题 1 解释游戏对象 GameObjects 和资源 Assets 的区别与联系 游戏对象 出现在游戏场景中 充当游戏的组件 游戏对象不做任何事情 需要特殊属性才能成为
  • Xcode Executable Not Found

    问题 Xcode编译项目报Executable Not Found的错误 Details Details Executable Not Found Domain DVTMachOErrorDomain Code 5 Recovery Sug
  • Rust 删除排序数组中的重复项

    力扣https leetcode cn com problems remove duplicates from sorted array 参考代码和注释 fn main let mut v Vec
  • Linux下Elasticsearch离线安装

    先去下载离线安装包 我这里是7 10 0 Past Releases of Elastic Stack Software Elastic 上传到 usr local下 解压 tar zxvf elasticsearch 7 10 0 lin
  • 【MATLAB】MATLAB打开后,提示内部崩溃,直接闪退关闭——解决方法

    问题描述 在第一次安装MATLAB软件时 正常使用 过了一段时间后 突然发现在命令行可以正常使用 但运行编译文件里的程序便会报 MathWorks 崩溃的错误 提示MATLAB遇到了内部问题 需要关闭 结果MATLAB自己闪退结束 解决方法
  • MATLAB(6)GUI应用介绍

    目录 GUI编辑器 控件 属性 回调函数 MATLAB常见的控件 普通按钮 切换按钮 可编辑文本 字符获取 字符显示 复选框 单选按钮 弹出式菜单 滑动条 列表框 表 坐标区 附录 各文件共享数据 保存 获取 GUI编辑器 MATLAB的G
  • 【问题记录】05 Host key for [ip] has changed and you have requested strict checking.Host key verification

    1 报错信息如下 为主机ip WARNING REMOTE HOST IDENTIFICATION HAS CHANGED IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY Someo

随机推荐

  • 操作系统实验——互斥与同步

    目录 1 SYSTEM V信号量 1 创建或打开 semget 2 申请或释放 semop 3 设置信号量 semctl 2 POSIX信号量 1 初始化 sem init 2 申请和释放 sem wait 3 销毁 sem destroy
  • 详细记录YOLACT实例分割ncnn实现

    点击上方 AI算法修炼营 选择加星标或 置顶 标题以下 全是干货 整理 公众号 深度学习与计算机视觉 作者 nihui 链接 https zhuanlan zhihu com p 128974102 本文转载自知乎 作者已授权 未经许可请勿
  • 《写给大家看的设计书(第4版)》读书笔记

    文章目录 前言 设计原则 亲密性 对齐 重复 对比 结语 前言 设计类的书籍看过一些 大多分为两类 一类是讲基础的 构图 明暗 色彩等基础理论 还有一类是分享介绍具体案里的 这两类书籍对于大多数并不想真正成为设计师的人来说很多时候并没有什么
  • 位置无关码介绍

    1 基本概念 应用程序必须经过编译 汇编和链接后才变成可执行文件 在链接时 要对所有目标文件进行地址重定位 建立符号引用规则 同时为变量 函数等分配运行地址 当程序执行时 系统必须把代码加载到链接时所指定的地址空间即链接地址 链接地址介绍在
  • 使用C写Python的模块

    使用C写Python的模块 2012 12 21 23 49 更新 邹业盛 概述 引入 Python h 头文件 编写包装函数 处理从 Python 传入的参数 实现逻辑功能 处理 C 中的返回值 注册函数 注册模块 编译 原文发于2010
  • 【开发工具】【make】make 3.82源码编译安装

    摘要 通过下载make 3 82源码 再编译机上安装make 3 82版本 解决make版本过高的问题 1 下载make 3 82 下载地址为 wget ftp ftp gnu org gnu make make 3 82 tar gz 我
  • 全局监控 click事件的四种方式

    本文主要给大家分享如何在全局上去监听 click 点击事件 并做些通用处理或是拦截 使用场景可能就是具体的全局防快速重复点击 或是通用打点分析上报 用户行为监控等 以下将以四种不同的思路和实现方式去监控全局的点击操作 由简单到复杂逐一讲解
  • Linux学习笔记-----网络编程套接字

    目录 一 概念 一 端口号概念 二 套接字概念 三 套接字 socket 编程接口 四 sockaddr结构 五 网络字节序 二 基于UDP的相关理解 一 UDP协议 二 编写简单的UDP服务端和客户端 三 小结 三 基于TCP的相关理解
  • iOS 自动构建命令——xcodebuild

    想想当初天天来到公司 每天需要做一件事就是打开Xcode打包ipa 上传到fir 日复一日月复一月年复一年的做着同样的事情 作为有志成为优秀工程师的我来说 这是必须要解决的问题 所以决定自动化解决问题 简介 xcodebuild 是苹果发布
  • Qt环境变量配置

    在桌面找到 此电脑 右击 找到属性 点击属性 找到高级系统设置 点击 选中环境变量 选中path 点击编辑 进入环境变量是这个样子的 下一步找到Qt安装的位置复制路径 这个是2015 64的 2015 32和2017 64的路径都要 然后
  • 如何选择期权品种,是做期货期货还是做期权

    有朋友问 国内的疫情目前得到较好的控制 经济也在逐渐恢复运行 国外的疫情在加重 也许会有部分国家经济做阶段停摆 假如上面的成立 我们是不是可以选择一种套利 买入国内经济需求会带动上涨的商品 卖出国外经济下滑会带动下跌的商品 如果这个方案可行
  • 分布式系统下的纠删码技术(一) -- Erasure Code (EC)

    近几个月主要参与一个分布式存储系统的纠删码部分 用于数据容错 纠删码在学术界出现比较早 现在ceph 微软的存储系统 Hadoop 3 0等都用了EC 文章会分为多篇 主要将Erasure Code LRC 以及相关的数学基础 作为学习总结
  • 前端技术搭建贪吃蛇小游戏(内含源码)

    功能介绍 以下是贪吃蛇小游戏的玩法和规则 游戏开始时 玩家控制一条小蛇在游戏区域内移动 通过吃食物来增加分数 小蛇的移动方向由玩家控制 可以使用键盘上的方向键来控制小蛇的移动方向 当小蛇吃到食物时 它会变长 并且玩家的分数会增加 如果小蛇撞
  • 拷贝构造函数(默认的,自定义的,什么时候一定要自定义,什么时候系统会自动调用)

    为什么有指针成员的类 要自定义拷贝构造函数 参考了 https blog csdn net caoshangpa article details 79226270 没有拷贝构造函数的类 系统会创建默认的拷贝构造函数 默认拷贝构造函数是浅拷贝
  • 【JSON 初级】

    概述 前后台数据交换的格式标准 一种优秀的 数据格式 采用键值对的方式 取数据 用键 优势 比XML更小 更快 更容易解析 JSON是存储和交换文本信息的语法 类似XML 工具 网上有校验json数据 并提示错误 将数据转化为json数据
  • 深入理解java虚拟机【并发编程缓存】

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 随着多核CPU的高速发展 为了充分利用硬件的计算资源 操作系统的并发多任务功能正变得越来越重要 但是CPU在进行计算时 还需要从内存读取输出 并 将计算结果存放到内存中 然
  • 【华为OD机试】分苹果(C++ Python Java)2023 B卷

    时间限制 C C 1秒 其他语言 2秒 空间限制 C C 262144K 其他语言524288K 64bit IO Format lld 语言限定 C clang11 C clang 11 Pascal fpc 3 0 2 Java jav
  • 闲聊:自动化到底是干什么的?

    很多人会问 自动化到底是干什么的 也许是因为这个专业所要学习和掌握的知识太庞杂了 以至于自动化被称之为万能胶 干什么都行 却又都不专业 很大一部分同学上到大二大三还不知道自己具体能做什么 迷茫中便选择了转行 希望还在迷茫中的低年级的同学看了
  • C++11变长模板解析(深入理解C++11)

    参考自 深入理解C 11 变长模版 变长函数和变长的模版参数 变长函数 double sum int n 求n个double数据之和 double sum 0 va list args 接受输入数据的数据结构 需声明stdarg h va
  • 3D游戏设计作业9:游戏智能

    坦克对战游戏 AI 设计 游戏截图 1 作业要求 从商店下载游戏 Kawaii Tank 或 其他坦克模型 构建 AI 对战坦克 具体要求 使用 感知 思考 行为 模型 建模 AI 坦克 场景中要放置一些障碍阻挡对手视线 坦克需要放置一个矩