欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

3D游戏作业——游戏智能

程序员文章站 2022-07-13 09:39:43
...

以下作业三选一

  1. 有趣 AR 小游戏制作
  2. 坦克对战游戏 AI 设计,从商店下载游戏:“Kawaii” Tank 或 其他坦克模型,构建 AI 对战坦克。具体要求使用“感知-思考-行为”模型,建模 AI 坦克 场景中要放置一些障碍阻挡对手视线 坦克需要放置一个矩阵包围盒触发器,以保证 AI坦克能使用射线探测对手方位 AI 坦克必须在有目标条件下使用导航,并能绕过障碍。(失去目标时策略自己思考) 实现人机对战
  3. P&D过河游戏智能帮助实现,程序具体要求:实现状态图的自动生成 讲解图数据在程序中的表示方法 利用算法实现下一步的计算 参考:P&D 过河游戏智能帮助实现

坦克对战游戏AI

从应用商店下载 ”Kawaii Tank“

3D游戏作业——游戏智能
使用“感知-思考-行为”模型,建模 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,今天实在是做不了了????)