3D游戏作业——游戏智能
以下作业三选一
- 有趣 AR 小游戏制作
- 坦克对战游戏 AI 设计,从商店下载游戏:“Kawaii” Tank 或 其他坦克模型,构建 AI 对战坦克。具体要求使用“感知-思考-行为”模型,建模 AI 坦克 场景中要放置一些障碍阻挡对手视线 坦克需要放置一个矩阵包围盒触发器,以保证 AI坦克能使用射线探测对手方位 AI 坦克必须在有目标条件下使用导航,并能绕过障碍。(失去目标时策略自己思考) 实现人机对战
- P&D过河游戏智能帮助实现,程序具体要求:实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 参考:P&D 过河游戏智能帮助实现
坦克对战游戏AI
从应用商店下载 ”Kawaii Tank“
使用“感知-思考-行为”模型,建模 AI 坦克:本次作业中,我利用RayCast来感知周围的环境,来判断是否视野内存在玩家,这是感知过程。如果存在玩家,则奔向玩家,并在距离玩家50米的距离停下射击,如果不存在玩家,则进行旋转探视,试图发现玩家,如果没有发现玩家则进行移动,在距离障碍物20米处停下,并调整方向和探视周围,进行新的移动过程,这是思考和行为过程。
场景中要放置一些障碍阻挡对手视线:Asset中含有丰富的场景,所以我利用了现成的障碍。
坦克需要放置一个矩阵包围盒触发器,以保证 AI 坦克能使用射线探测对手方位:包中的坦克模型自带相关触发器和刚体。
AI 坦克必须在有目标条件下使用导航,并能绕过障碍。(失去目标时策略自己思考):具体过程如上
实现人机对战:已实现
修改的是Wheel_Control_CS.cs
using UnityEngine;
using System.Collections;
#if UNITY_ANDROID || UNITY_IPHONE
using UnityStandardAssets.CrossPlatformInput;
#endif
// This script must be attached to "MainBody".
namespace ChobiAssets.KTP
{
public class Wheel_Control_CS : MonoBehaviour
{
[ Header ("Driving settings")]
[ Tooltip ("Torque added to each wheel.")] public float wheelTorque = 3000.0f; // Reference to "Wheel_Rotate".
[ Tooltip ("Maximum Speed (Meter per Second)")] public float maxSpeed = 7.0f; // Reference to "Wheel_Rotate".
[ Tooltip ("Rate for ease of turning."), Range (0.0f, 2.0f)] public float turnClamp = 0.8f;
[ Tooltip ("'Solver Iteration Count' of all the rigidbodies in this tank.")] public int solverIterationCount = 7;
// Reference to "Wheel_Rotate".
[HideInInspector] public float leftRate;
[HideInInspector] public float rightRate;
float idle = 0;
Rigidbody thisRigidbody;
bool isParkingBrake = false;
float lagCount;
float speedStep;
float autoParkingBrakeVelocity = 0.5f;
float autoParkingBrakeLag = 0.5f;
ID_Control_CS idScript;
bool UpArrow = false;
bool DownArrow = false;
bool X=false;
bool LeftArrow=false;
bool RightArrow=false;
/* for reducing Calls.
Wheel_Rotate_CS[] rotateScripts;
*/
void Awake ()
{
this.gameObject.layer = 11; // Layer11 >> for MainBody.
thisRigidbody = GetComponent < Rigidbody > ();
thisRigidbody.solverIterationCount = solverIterationCount;
/* for reducing Calls.
rotateScripts = GetComponentsInChildren <Wheel_Rotate_CS> ();
*/
}
void Update ()
{
if (idScript.isPlayer) {
#if UNITY_ANDROID || UNITY_IPHONE
Mobile_Input ();
#else
Desktop_Input ();
#endif
}
else{
// Debug.Log(transform.position);
RaycastHit hit;
Vector3 palyerposition = new Vector3(0,0,0);
bool found = false;
// for(float x = -1.00f; x<=1.00f;x+=0.001f){
// double z = System.Math.Sqrt( 1- x*x);
// if(Physics.Raycast(this.transform.position,new Vector3(x,0,(float)z), out hit)){
// try{
// if(hit.rigidbody.gameObject.tag=="Player"){
// found=true;
// palyerposition=hit.rigidbody.gameObject.transform.position;
// break;
// }
// }
// catch{
// }
// }
// }
float i = 1.00f;
while(i>=-1.00f){
i-=0.01f;
if(Physics.Raycast(this.transform.position,new Vector3(1,0,i), out hit)){
try{
if(hit.rigidbody.gameObject.tag=="Player"){
found=true;
palyerposition=hit.rigidbody.gameObject.transform.position;
break;
}
}
catch{
}
}
if(Physics.Raycast(this.transform.position,new Vector3(-1,0,i), out hit)){
//Debug.Log("9732");
try{
if(hit.rigidbody.gameObject.tag=="Player"){
//Debug.Log("9527");
found=true;
palyerposition=hit.rigidbody.gameObject.transform.position;
break;
}
}
catch{
}
}
}
if(found){
idle=0;
Vector3 forward = this.transform.InverseTransformPoint(palyerposition);
Debug.Log(forward);
if(forward.x>0){
LeftArrow=true;
RightArrow=false;
} else if(forward.x<0){
LeftArrow=false;
RightArrow=true;
}
if(forward.z>0){
UpArrow=true;
DownArrow=false;
}
var dis = Vector3.Distance(palyerposition, this.transform.position);
if(dis<50){
UpArrow=false;
idScript.fireButton=true;
}
else{
UpArrow=true;
idScript.fireButton=false;
}
}
else{
idle+=1;
if(idle<100){
Debug.Log(idle);
LeftArrow=true;
RightArrow=false;
UpArrow=false;
DownArrow=false;
idScript.fireButton=false;
X=true;
}
else{
Goaway();
}
}
// if (found){
// // Debug.Log(palyerposition);
// Vector3 forward = this.transform.InverseTransformPoint(palyerposition);
// Debug.Log(forward);
// if(forward.x > 0){
// RightArrow=true;
// LeftArrow=false;
// }
// else if(forward.x<0){
// RightArrow=false;
// LeftArrow=true;
// }
// else{
// RightArrow=false;
// LeftArrow=false;
// }
// var dis = Vector3.Distance(palyerposition, this.transform.position);
// if(dis>50){
// UpArrow=true;
// }
// else{
// UpArrow=false;
// }
// //float dis = Vector3.Distance(palyerposition, this.transform.position)
// }
// else{
// idScript.fireButton = false;
// LeftArrow=false;
// RightArrow=false;
// X = true;
// }
AI_Input();
}
}
#if UNITY_ANDROID || UNITY_IPHONE
void Mobile_Input ()
{
if (CrossPlatformInputManager.GetButtonDown ("Up")) {
speedStep += 0.5f;
speedStep = Mathf.Clamp (speedStep, -1.0f, 1.0f);
} else if (CrossPlatformInputManager.GetButtonDown ("Down")) {
speedStep -= 0.5f;
speedStep = Mathf.Clamp (speedStep, -1.0f, 1.0f);
}
float vertical = speedStep;
float horizontal = 0.0f;
if (CrossPlatformInputManager.GetButton ("Left")) {
horizontal = Mathf.Lerp (-turnClamp, -1.0f, Mathf.Abs (vertical / 1.0f));
} else if (CrossPlatformInputManager.GetButton ("Right")) {
horizontal = Mathf.Lerp (turnClamp, 1.0f, Mathf.Abs (vertical / 1.0f));
}
if (vertical < 0.0f) {
horizontal = -horizontal; // like a brake-turn.
}
leftRate = Mathf.Clamp (-vertical - horizontal, -1.0f, 1.0f);
rightRate = Mathf.Clamp (vertical - horizontal, -1.0f, 1.0f);
}
#else
void AI_Input()
{
if(UpArrow){
speedStep += 0.5f;
speedStep = Mathf.Clamp (speedStep, -1.0f, 1.0f);
} else if(DownArrow){
speedStep -= 0.5f;
speedStep = Mathf.Clamp (speedStep, -1.0f, 1.0f);
} else if(X){
speedStep = 0.0f;
}
float vertical = speedStep;
float horizontal = 0;
if(LeftArrow){
horizontal += 0.5f;
} else if(RightArrow){
horizontal -= 0.5f;
}
float clamp = Mathf.Lerp (turnClamp, 1.0f, Mathf.Abs (vertical / 1.0f));
horizontal = Mathf.Clamp (horizontal, -clamp, clamp);
if (vertical < 0.0f) {
horizontal = -horizontal; // like a brake-turn.
}
leftRate = Mathf.Clamp (-vertical - horizontal, -1.0f, 1.0f);
rightRate = Mathf.Clamp (vertical - horizontal, -1.0f, 1.0f);
}
void Desktop_Input ()
{
if (Input.GetKeyDown (KeyCode.UpArrow) || Input.GetKeyDown (KeyCode.W)) {
speedStep += 0.5f;
speedStep = Mathf.Clamp (speedStep, -1.0f, 1.0f);
} else if (Input.GetKeyDown (KeyCode.DownArrow) || Input.GetKeyDown (KeyCode.S)) {
speedStep -= 0.5f;
speedStep = Mathf.Clamp (speedStep, -1.0f, 1.0f);
} else if (Input.GetKeyDown (KeyCode.X)) {
speedStep = 0.0f;
}
float vertical = speedStep;
float horizontal = Input.GetAxis ("Horizontal");
float clamp = Mathf.Lerp (turnClamp, 1.0f, Mathf.Abs (vertical / 1.0f));
horizontal = Mathf.Clamp (horizontal, -clamp, clamp);
if (vertical < 0.0f) {
horizontal = -horizontal; // like a brake-turn.
}
leftRate = Mathf.Clamp (-vertical - horizontal, -1.0f, 1.0f);
rightRate = Mathf.Clamp (vertical - horizontal, -1.0f, 1.0f);
}
#endif
void FixedUpdate ()
{
// Auto Parking Brake using 'RigidbodyConstraints'.
if (leftRate == 0.0f && rightRate == 0.0f) {
float velocityMag = thisRigidbody.velocity.magnitude;
float angularVelocityMag = thisRigidbody.angularVelocity.magnitude;
if (isParkingBrake == false) {
if (velocityMag < autoParkingBrakeVelocity && angularVelocityMag < autoParkingBrakeVelocity) {
lagCount += Time.fixedDeltaTime;
if (lagCount > autoParkingBrakeLag) {
isParkingBrake = true;
thisRigidbody.constraints = RigidbodyConstraints.FreezePositionX | RigidbodyConstraints.FreezePositionZ | RigidbodyConstraints.FreezeRotationY;
}
}
} else {
if (velocityMag > autoParkingBrakeVelocity || angularVelocityMag > autoParkingBrakeVelocity) {
isParkingBrake = false;
thisRigidbody.constraints = RigidbodyConstraints.None;
lagCount = 0.0f;
}
}
} else {
isParkingBrake = false;
thisRigidbody.constraints = RigidbodyConstraints.None;
lagCount = 0.0f;
}
/* for reducing Calls.
for (int i = 0; i < rotateScripts.Length; i++) {
rotateScripts [i].FixedUpdate_Me ();
}
*/
}
void Goaway(){
RaycastHit hit;
Vector3 palyerposition = new Vector3(0,0,0);
bool found = false;
float i = 1.00f;
while(i>=-1.00f){
i-=0.01f;
if(Physics.Raycast(this.transform.position,new Vector3(1,0,i), out hit)){
try{
if(hit.rigidbody!=null){
if(hit.rigidbody.gameObject.tag == "Myself"){
continue;
}
found=true;
palyerposition=hit.rigidbody.gameObject.transform.position;
break;
}
}
catch{
}
}
if(Physics.Raycast(this.transform.position,new Vector3(-1,0,i), out hit)){
//Debug.Log("9732");
try{
if(hit.rigidbody!=null){
if(hit.rigidbody.gameObject.tag == "Myself"){
continue;
}
//Debug.Log("9527");
found=true;
palyerposition=hit.rigidbody.gameObject.transform.position;
break;
}
}
catch{
}
}
}
if(found==false){
UpArrow=true;
}
else{
var dis = Vector3.Distance(palyerposition, this.transform.position);
Debug.Log("dis");
Debug.Log(dis);
if(dis>50){
UpArrow=true;
}
else{
idle=0;
}
}
}
void Destroy ()
{ // Called from "Damage_Control_CS".
StartCoroutine ("Disable_Constraints");
}
IEnumerator Disable_Constraints ()
{
// Disable constraints of MainBody's rigidbody.
yield return new WaitForFixedUpdate (); // This wait is required for PhysX.
thisRigidbody.constraints = RigidbodyConstraints.None;
Destroy (this);
}
void Get_ID_Script (ID_Control_CS tempScript)
{
idScript = tempScript;
// if(idScript.isPlayer==false){
// idScript.fireButton=true;
// Debug.Log("i am here");
// }
}
void Pause (bool isPaused)
{ // Called from "Game_Controller_CS".
this.enabled = !isPaused;
}
}
}
总结
演示视频:
Mytank
Gitee地址
这次的作业时间比较仓促,做的不是很好,这次作业参考了博客,主要参考了它是怎么利用 ‘Kawaii Tank’的相关库,但是看完了之后感觉思路就很狭窄了,做来做去都和原博客比较像,于是就脱离原博客进行改进,第一个改进的想法是实现360度感知敌人,原博客是只能发现正面的敌人,我尝试利用RayCast来360发射射线,但无论我怎么改都不行,无论是用圆周方程还是用4个90度拼接,都只能实现正面探测的功能,我觉得可能是游戏模型底层的一些代码导致的,改了大概5,6个小时,最后时间不够只能放弃。第二个改进之处是我实现了没有发现玩家时的AI自动寻路过程,我实现其自动旋转寻找玩家和自动绕开相关障碍物进行巡逻的功能,效果在视频中有体现。
这次作业做得比较仓促,参考了较多上面那篇博客,如果TA觉得工作量不够请告诉我,我可以之后再做一份!(明晚有3个DDL,今天实在是做不了了????)
上一篇: 3D游戏设计作业4