1、改进飞碟(Hit UFO)游戏:
游戏内容要求:
按 adapter模式 设计图修改飞碟游戏
使它同时支持物理运动与运动学(变换)运动
这次的作业要求改进上次的UFO,上次的UFO游戏我没有采用动作分离,只采用了MVC模式,这次由于需要按照老师的要求实现adapter模式,所以我必须实现动作分离,动作分离的UML图根据老师的来:
适配器UML图:
SSActionMagnager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSActionManager : MonoBehaviour {
private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction> ();
private List<SSAction> waitingAdd = new List<SSAction>();
private List<int> waitingDelete = new List<int>();
protected void Update() {
foreach (SSAction ac in waitingAdd) {
actions[ac.GetInstanceID()] = ac;
}
waitingAdd.Clear();
foreach (KeyValuePair<int, SSAction> kv in actions) {
SSAction ac = kv.Value;
if (ac.destory) {
waitingDelete.Add(ac.GetInstanceID());
} else if (ac.enable) {
ac.Update();
}
}
foreach(int key in waitingDelete) {
SSAction ac = actions[key];
actions.Remove(key);
DestroyObject(ac);
}
waitingDelete.Clear();
}
public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager) {
action.gameobject = gameobject;
action.transform = gameobject.transform;
action.callback = manager;
waitingAdd.Add(action);
action.Start();
}
protected void Start() {}
}
SSAction:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject {
public bool enable = true;
public bool destory = false;
public GameObject gameobject{ get; set;}
public Transform transform{ get; set; }
public ISSActionCallback callback{ get; set; }
protected SSAction() {}
public virtual void Start () {
throw new System.NotImplementedException();
}
public virtual void Update () {
throw new System.NotImplementedException ();
}
}
适配器:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Adapter : SSActionManager, ISSActionCallback
{
public bool running;
public virtual void fly(GameObject ufo,float speed){
}
public void SSActionEvent(SSAction source,SSActionEventType events = SSActionEventType.Completed,
int intParam = 0,
string strParam = null,
Object objectParam = null) {
}
public void pause(){
running=!running;
}
// Start is called before the first frame update
void Start()
{
running=true;
}
// Update is called once per frame
void Update()
{
if(running){
base.Update();
}
}
}
CCActionManager(第一个运动学):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager : Adapter
{
public override void fly(GameObject ufo,float speed){
CCFlyAction action = CCFlyAction.GetSSAction(speed);
this.RunAction(ufo, action, this);
}
}
FlyActoin:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCFlyAction : SSAction
{
public float speed;
// Start is called before the first frame update
public static CCFlyAction GetSSAction(float speed){
CCFlyAction action = ScriptableObject.CreateInstance<CCFlyAction>();
action.speed=speed;
return action;
}
public override void Start()
{
}
// Update is called once per frame
public override void Update()
{
// Debug.Log("asdasd");
Vector3 v = new Vector3(this.gameobject.transform.localRotation.x,this.gameobject.transform.localRotation.y,this.gameobject.transform.localRotation.z);
this.transform.position = Vector3.MoveTowards (this.transform.position, this.transform.position + v*1000000 , speed * Time.deltaTime);
if(this.gameobject.transform.position.x > 10 || this.gameobject.transform.position.x < -10){
this.destory=true;
this.callback.SSActionEvent (this);
}
if(this.gameobject.transform.position.y > 10 || this.gameobject.transform.position.y < -10){
this.destory=true;
this.callback.SSActionEvent (this);
}
if(this.gameobject.transform.position.z < -10){
this.destory=true;
this.callback.SSActionEvent (this);
}
}
}
CCActionManager1(第二个动力学):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCActionManager2 : Adapter
{
public override void fly(GameObject ufo,float speed){
CCFlyAction2 action = CCFlyAction2.GetSSAction(speed);
this.RunAction(ufo, action, this);
}
}
FlyAction2:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCFlyAction2 : SSAction
{
public float speed;
// Start is called before the first frame update
public static CCFlyAction2 GetSSAction(float speed){
CCFlyAction2 action = ScriptableObject.CreateInstance<CCFlyAction2>();
action.speed=speed;
return action;
}
public override void Start()
{
}
// Update is called once per frame
public override void Update()
{
// Debug.Log("asdasd");
Vector3 v = new Vector3(this.gameobject.transform.localRotation.x,this.gameobject.transform.localRotation.y,this.gameobject.transform.localRotation.z);
// if(this.gameobject.GetComponent<Rigidbody>())
// Debug.Log(1238888);
this.gameobject.GetComponent<Rigidbody>().AddForce(v*1f*speed);
// this.gameobject.GetComponent<Rigidbody>().AddForce(9.8f * Vector3.down);
if(this.gameobject.transform.position.x > 10 || this.gameobject.transform.position.x < -10){
this.destory=true;
this.callback.SSActionEvent (this);
}
if(this.gameobject.transform.position.y > 10 || this.gameobject.transform.position.y < -10){
this.destory=true;
this.callback.SSActionEvent (this);
}
if(this.gameobject.transform.position.z < -10){
this.destory=true;
this.callback.SSActionEvent (this);
}
}
}
以上是这次改进的一些代码,其他诸如MVC架构的基本代码都没有大改动,单例工厂模式的代码也进行了一些小的改动。
我的动力学构思是这样的:
给每个UFO设置好mesh碰撞体,并分配一个钢体组件,该碰撞体全部设置为trigger属性,在运动学操控时,不对碰撞行为进行控制,而在动力学时,会相互反弹,并且在运动学时给的是恒定速度,而动力学给的是一个动力,这就是他们的区别。
演示视频见bilibili
2、打靶游戏(可选作业):
游戏内容要求:
靶对象为 5 环,按环计分;
箭对象,射中后要插在靶上
增强要求:射中后,箭对象产生颤抖效果,到下一次射击 或 1秒以后
游戏仅一轮,无限 trials;
增强要求:添加一个风向和强度标志,提高难度
先制作五层靶子
有五个圆柱体构成,每个圆柱体都有自己的mesh碰撞体来判断碰撞事件,打靶游戏采用了简单的MVC模式。
箭的构成:
钢体的重心集中在箭头,判断脚本也挂在箭头。
Controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IUserAction
{
void GameOver();
}
public class View : MonoBehaviour
{
private Model action;
private GUIStyle fontstyle = new GUIStyle();
private GUIStyle fontstyle1 = new GUIStyle();
// Start is called before the first frame update
void Start()
{
action = Controller.getInstance().currentModel as Model;
fontstyle.fontSize = 30;
fontstyle.normal.textColor = Color.yellow;
fontstyle1.fontSize = 30;
fontstyle1.normal.textColor = Color.yellow;
}
// Update is called once per frame
void Update()
{
}
public void OnGUI(){
GUI.Label(new Rect(10,40,200,200),"Score: " + Model.score, fontstyle);
if(Model.wind_direction == 0){
GUI.Label(new Rect(10,100,200,200),"Wind: Left with strength " + Model.strength, fontstyle);
}
else{
GUI.Label(new Rect(10,100,200,200),"Wind: Right with strength " + Model.strength, fontstyle);
}
}
}
Model:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ForModel{
void LoadResources();
void Pause();
void Resume();
}
public class Model : MonoBehaviour, ForModel
{
public static int score;
public static bool shooted=false;
public static int wind_direction = 0;
public static int strength = 0;
Controller controller;
void Awake(){
controller = Controller.getInstance();
controller.setFPS(60);
controller.currentModel = this;
controller.running=true;
LoadResources();
score=0;
}
public void Start(){
float num = Random.Range(0f,5f);
float num2 = Random.Range(0f, 2f);
if(num<1){
strength = 1;
}
else if(num < 2){
strength = 2;
}
else if(num < 3){
strength = 3;
}
else if(num < 4){
strength = 4;
}
else if(num < 5){
strength = 5;
}
if(num2 < 1){
wind_direction = 0;
}
else{
wind_direction = 1;
}
}
public void Restart(){
}
public void Resume(){
}
public void Pause(){
controller.running=!controller.running;
}
public void LoadResources(){
}
// Start is called before the first frame update
// Update is called once per frame
void Update()
{
}
}
View:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IUserAction
{
void GameOver();
}
public class View : MonoBehaviour
{
private Model action;
private GUIStyle fontstyle = new GUIStyle();
private GUIStyle fontstyle1 = new GUIStyle();
// Start is called before the first frame update
void Start()
{
action = Controller.getInstance().currentModel as Model;
fontstyle.fontSize = 30;
fontstyle.normal.textColor = Color.yellow;
fontstyle1.fontSize = 30;
fontstyle1.normal.textColor = Color.yellow;
}
// Update is called once per frame
void Update()
{
}
public void OnGUI(){
GUI.Label(new Rect(10,40,200,200),"Score: " + Model.score, fontstyle);
if(Model.wind_direction == 0){
GUI.Label(new Rect(10,100,200,200),"Wind: Left with strength " + Model.strength, fontstyle);
}
else{
GUI.Label(new Rect(10,100,200,200),"Wind: Right with strength " + Model.strength, fontstyle);
}
}
}
挂在箭上面的判断脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Tit : MonoBehaviour
{
public Transform tf;
public bool shooted;
// Start is called before the first frame update
void Start()
{
// Vector3 v = new Vector3(5,1,1);
this.gameObject.GetComponent<Rigidbody>().centerOfMass = tf.localPosition;
shooted=false;
// this.gameObject.GetComponent<Rigidbody>().AddForce(1f*v);
// this.transform.LookAt(this.transform.position+v*100);
// this.gameObject.GetComponent<Rigidbody>().AddTorque(v*1f);
}
// Update is called once per frame
void Update()
{
if(this.transform.position.z > 0.5){
Model.shooted = true;
}
if (Input.GetKeyDown(KeyCode.Space) && Model.shooted==true){
this.gameObject.GetComponent<Rigidbody>().isKinematic = true;
Vector3 v = new Vector3(0, 0, -10);
Model.shooted=false;
this.gameObject.GetComponent<Rigidbody>().useGravity = false;
this.gameObject.GetComponent<Rigidbody>().isKinematic = false;
this.transform.position=v;
float num = Random.Range(0f,5f);
float num2 = Random.Range(0f, 2f);
if(num<1){
Model.strength = 1;
}
else if(num < 2){
Model.strength = 2;
}
else if(num < 3){
Model.strength = 3;
}
else if(num < 4){
Model.strength = 4;
}
else if(num < 5){
Model.strength = 5;
}
if(num2 < 1){
Model.wind_direction = 0;
}
else{
Model.wind_direction = 1;
}
}
if(Model.shooted==false){
Vector3 v = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
this.transform.LookAt(v);
}
if (Input.GetButtonDown("Fire1")) {
this.gameObject.GetComponent<Rigidbody>().useGravity = true;
Vector3 v = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
this.gameObject.GetComponent<Rigidbody>().AddForce(10f*v,ForceMode.Impulse);
if(Model.wind_direction == 0){
Vector3 vv = new Vector3(-1, 0,0);
this.gameObject.GetComponent<Rigidbody>().AddForce(25f*Model.strength*vv,ForceMode.Force);
}
else if(Model.wind_direction == 1){
Vector3 vv = new Vector3(1, 0,0);
this.gameObject.GetComponent<Rigidbody>().AddForce(25f*Model.strength*vv,ForceMode.Force);
}
this.transform.LookAt(v);
Debug.Log (v);
}
}
void OnTriggerEnter(Collider other)
{
this.gameObject.GetComponent<Rigidbody>().isKinematic = true;
// gameObject.SetActive(false);
// Debug.Log(1111);
// if(other.gameObject.tag == "target")
// {
// gameObject.transform.parent.gameObject.GetComponent<Rigidbody>().isKinematic = true;
// gameObject.SetActive(false);
// int points = other.gameObject.name[other.gameObject.name.Length - 1] - '0';
// scene.showTips(points);
// scene.addScore(points);
// }
}
}
游戏仅一轮,通过按空格回收弓箭,演示视频在bilibili(还没过审核。。。)
游戏射箭是通过对弓箭施加瞬间力实现的,弓箭收左右风力的影响,游戏中有提示。