文章目录
Hit UFO改进版
一、改进要求
- 按 adapter模式 设计图修改飞碟游戏
- 使它同时支持物理运动与运动学(变换)运动
二、MVC图
三、gitee
四、程序实现
本次作业使用上一次作业Hit UFO来进行改进,主要实现部分为增加了适配器和物理模式的切换,其余部分几乎没有改变,详情见上一次博客 hit ufo 基础版
4.1 FirstController
沿用之前的代码,只不过增加了一个用户操作changeStyle,在其中要调用飞碟工厂类中的rmgrivity函数来取消调刚体重力,和刚体速度
public void changeStyle()
{
if (isPhy)
{
disk_factory.rmgrivity();
}
isPhy = !isPhy;
}
4.2 Interface
增加了用户操作changeStyle
public interface IUserAction
{
void Hit(Vector3 pos);
int GetScore();
void GameOver();
void ReStart();
void BeginGame();
void changeStyle();
}
4.3 ActionManagerAdapter
作为插入到原有的动作管理类上层的一个文件,决定着调用哪一种动作完成请求
public class ActionManagerAdapter : MonoBehaviour, IActionManager
{
public FlyActionManager action_manager;
public PhysisFlyActionManager phy_action_manager;
public void playDisk(GameObject disk, float angle, float power, bool isPhy)
{
Debug.Log("phy is : !" + isPhy);
if (isPhy)
{
Debug.Log("phy");
//disk.AddComponent<>
phy_action_manager.UFOFly(disk, angle, power);
}
else
{
Debug.Log("not phy");
action_manager.UFOFly(disk, angle, power);
}
}
// Use this for initialization
void Start()
{
action_manager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
phy_action_manager = gameObject.AddComponent<PhysisFlyActionManager>() as PhysisFlyActionManager;
}
}
4.4 PhsisFlyActionManager
物理动作管理类,用于管理物理动作
public class PhysisFlyActionManager : SSActionManager
{
public PhysisUFOFlyAction fly; //飞碟飞行的动作
//public FirstController scene_controller; //当前场景的场景控制器
protected void Start()
{
}
//飞碟飞行
public void UFOFly(GameObject disk, float angle, float power)
{
fly = PhysisUFOFlyAction.GetSSAction(disk.GetComponent<DiskData>().direction, angle, power);
this.RunAction(disk, fly, this);
}
}
4.5 PhysisUFOFlyAction
设定飞碟物理模式下的运动,这里通过添加重力以及velocity实现
public class PhysisUFOFlyAction : SSAction
{
private Vector3 start_vector; //初速度向量
public float power;
private PhysisUFOFlyAction() { }
public static PhysisUFOFlyAction GetSSAction(Vector3 direction, float angle, float power)
{
//初始化物体将要运动的初速度向量
PhysisUFOFlyAction action = CreateInstance<PhysisUFOFlyAction>();
if (direction.x == -1)
{
action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
}
else
{
action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
}
action.power = power;
return action;
}
public override void FixedUpdate()
{
//判断是否超出范围
if (this.transform.position.y < -10)
{
this.destroy = true;
this.callback.SSActionEvent(this);
}
}
public override void Update() { }
public override void Start()
{
//使用重力以及给一个初速度
gameobject.GetComponent<Rigidbody>().velocity = power / 20 * start_vector;
gameobject.GetComponent<Rigidbody>().useGravity = true;
}
}
4.5 DiskFactory
工厂类中增加了rmgrivity函数,用于从物理模式切换到运动学模式时,消除物理学的刚体组件中的参数
public void rmgrivity()
{
for (int i = 0; i < used.Count; i++)
{
used[i].GetComponent<Rigidbody>().useGravity = false;
used[i].GetComponent<Rigidbody>().velocity = 0*Vector3.zero;
}
for (int i = 0; i < free.Count; i++)
{
free[i].GetComponent<Rigidbody>().useGravity = false;
free[i].GetComponent<Rigidbody>().velocity = 0 * Vector3.zero;
}
}
五、效果展示
- 物理模式:
- 运动学模式:
射箭游戏
一、实现功能:
- 靶对象为 5 环,按环计分;
- 箭对象,射中后要插在靶上
- 射中后,箭对象产生颤抖效果,持续八秒
- 游戏仅一轮,无限 trials
- 添加一个风向和强度标志,提高难度
- 模拟真实射箭过程
- 按住右键调整弓箭方向
- 按住左键进行蓄力,松开后发射,根据蓄力时间决定力度
二、程序实现:
2.1 SceneController & interface
首先定义用户动作,并在SceneController中定义动作调用
namespace Scene
{
public interface IUserAction
{
void getArrow();
void shootArrow(Vector3 mousePos,float xuli);
void changeArrowpos(Vector3 mousePos);
}
public interface ISceneController
{
void addScore(int point);
//void showTips(int point);
void changeWind();
int getWindDirection();
int getWindStrength();
bool ifReadyToShoot();
}
public class SceneController : System.Object, IUserAction, ISceneController
{
private static SceneController instance;
private GameStateController gameStateController;
private ArrowController arrowController;
public static SceneController getInstance()
{
if (instance == null) instance = new SceneController();
return instance;
}
public void setGameController(GameStateController gsc)
{
gameStateController = gsc;
}
public void setArrowController(ArrowController _arrowController)
{
arrowController = _arrowController;
}
public void addScore(int point)
{
gameStateController.addScore(point);
}
public void changeWind()
{
gameStateController.changeWind();
}
public void getArrow()
{
arrowController.getArrow();
}
public int getWindDirection()
{
return gameStateController.getWindDirec();
}
public int getWindStrength()
{
return gameStateController.getWindStrength();
}
public bool ifReadyToShoot()
{
return arrowController.ifReadyToShoot();
}
public void shootArrow(Vector3 mousePos,float xuli)
{
arrowController.shootArrow(mousePos,xuli);
}
public void changeArrowpos(Vector3 mousePos)
{
arrowController.changeArrowpos(mousePos);
}
}
2.2 GameStateController
这里完成积分工作和风向提示,在OnGUI中更新数据
public class GameStateController : MonoBehaviour {
public int i = 0;
private int score = 0, windDir = 0, windStrength = 0;
GUIStyle bold_style = new GUIStyle();
GUIStyle score_style = new GUIStyle();
GUIStyle text_style = new GUIStyle();
GUIStyle over_style = new GUIStyle();
private int high_score = 0;
//private const float TIPS_SHOW_TIME = 0.5f;
//private GameObject canvas, scoreText, tipsText, windText;
private SceneController scene;
private string[] windDirectionArray;
// Use this for initialization
void Start () {
scene = SceneController.getInstance();
scene.setGameController(this);
windDirectionArray = new string[8] { "↑", "↗", "→", "↘", "↓", "↙", "←", "↖" };
changeWind();
}
public void changeWind()
{
windDir = UnityEngine.Random.Range(0,8);
windStrength = UnityEngine.Random.Range(0, 8);
}
internal void addScore(int point)
{
i++;
if (i % 2 == 0)
high_score += point;
changeWind();
}
// Update is called once per frame
void Update () {
}
void OnGUI()
{
bold_style.normal.textColor = new Color(1, 0, 0);
bold_style.fontSize = 16;
text_style.normal.textColor = new Color(0, 0, 0, 1);
text_style.fontSize = 16;
score_style.normal.textColor = new Color(1, 0, 1, 1);
score_style.fontSize = 16;
over_style.normal.textColor = new Color(1, 0, 0);
over_style.fontSize = 25;
GUI.Label(new Rect(10, 5, 200, 50), "分数:" +high_score, text_style);
GUI.Label(new Rect(10, 105, 200, 50), "Wind: " + windDirectionArray[windDir] + " x" + windStrength, text_style);
}
internal int getWindDirec()
{
return windDir;
}
internal int getWindStrength()
{
return windStrength;
}
}
2.3 UserInterface
检测鼠标的动作,这里定义了一个时间用于判断蓄力,并检测鼠标左右键的按下和松开,来进行角度调整和拉弓射箭,模拟真实射箭环境
public class UserInterface : MonoBehaviour {
private IUserAction action;
private ISceneController scene;
bool gameStart = false;
int timeCount = 0;
bool left = false;
bool right = false;
bool timeflag;
float xuli;
// Use this for initialization
void Start () {
action = SceneController.getInstance() as IUserAction;
scene = SceneController.getInstance() as ISceneController;
}
void Update () {
if (!gameStart)
{
action.getArrow();
gameStart = true;
}
if (timeCount%75==0) action.getArrow();
if (Input.GetMouseButtonUp(1))
{
right = false;
}
if (scene.ifReadyToShoot())
{
//
if (Input.GetMouseButtonDown(1))
{
right = true;
}
if (right)
{
Vector3 mousePos = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
action.changeArrowpos(mousePos);
}
if (Input.GetMouseButtonDown(0))
{
timeflag = true;
xuli = 0;
}
//左键松开,射出弓箭
if (Input.GetMouseButtonUp(0))
{
Vector3 mousePos = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
action.shootArrow(mousePos,xuli);
timeCount = 0;
timeflag = false;
}
if (timeflag)
{
xuli += Time.deltaTime;
}
}
timeCount++;
}
}
2.4 ArrowFactory
弓箭的工厂类,定义弓箭的产生和获取
public class ArrowFactory : System.Object
{
private static ArrowFactory instance;
private GameObject ArrowPrefabs;
private List<GameObject> usingArrowList = new List<GameObject>();
private List<GameObject> freeArrowList = new List<GameObject>();
private Vector3 INITIAL_POS = new Vector3(0, 0, -19);
public static ArrowFactory getInstance()
{
if (instance == null) instance = new ArrowFactory();
return instance;
}
public void init(GameObject _arrowPrefabs)
{
ArrowPrefabs = _arrowPrefabs;
}
internal void detectArrowsReuse()
{
for(int i = 0; i < usingArrowList.Count; i++)
{
if(usingArrowList[i].transform.position.y <= -8)
{
usingArrowList[i].GetComponent<Rigidbody>().isKinematic = true;
usingArrowList[i].SetActive(false);
usingArrowList[i].transform.position = INITIAL_POS;
freeArrowList.Add(usingArrowList[i]);
usingArrowList.Remove(usingArrowList[i]);
i--;
SceneController.getInstance().changeWind();
}
}
}
internal GameObject getArrow()
{
if(freeArrowList.Count == 0)
{
GameObject newArrow = Camera.Instantiate(ArrowPrefabs);
usingArrowList.Add(newArrow);
return newArrow;
}
else
{
GameObject oldArrow = freeArrowList[0];
freeArrowList.RemoveAt(0);
oldArrow.SetActive(true);
usingArrowList.Add(oldArrow);
return oldArrow;
}
}
}
2.5 ArrowCollider
放在箭头上的脚本,用于判断是否碰触到板子,然后为弓箭添加颤抖组件
public class ArrowCollider : MonoBehaviour {
private ISceneController scene;
float radian = 0; // 弧度
float per_radian = 3f; // 每次变化的弧度
float radius = 0.01f; // 半径
Vector3 old_pos; // 开始时候的坐标
public float left_time = 8f; //动作持续时间
void Start () {
scene = SceneController.getInstance() as ISceneController;
}
void Update () {
}
[System.Obsolete]
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "target")
{
gameObject.transform.parent.gameObject.GetComponent<Rigidbody>().isKinematic = true;
gameObject.SetActive(false);
int points = 0;
if(other.gameObject.name.ToString() == "1")
{
points = 1;
}
if (other.gameObject.name.ToString() == "2")
{
points = 2;
}
if (other.gameObject.name.ToString() == "3")
{
points = 3;
}
if (other.gameObject.name.ToString() == "4")
{
points = 4;
}
if (other.gameObject.name.ToString() == "5")
{
points = 5;
}
if (other.gameObject.name.ToString() == "6")
{
points = 6;
}
scene.addScore(points);
//scene.showTips(points);
gameObject.transform.parent.FindChild("Body").gameObject.AddComponent<bodytremble>();
Debug.Log("pengzhuang");
}
}
}
2.6 ArrowController
增加风力,并控制弓箭和靶子的产生和选择
public class ArrowController : MonoBehaviour {
public GameObject TargetPrefabs, ArrowPrefabs;
private GameObject holdingArrow, target;
private const int SPEED = 40;
private SceneController scene;
private Vector3[] winds = new Vector3[8]
{
new Vector3(0, 1, 0), new Vector3(1,1,0), new Vector3(1,0,0), new Vector3(1,-1,0), new Vector3(0,-1,0), new Vector3(-1,-1,0), new Vector3(-1,0,0), new Vector3(-1,1,0)
};
private void Awake()
{
ArrowFactory.getInstance().init(ArrowPrefabs);
}
// Use this for initialization
void Start () {
scene = SceneController.getInstance();
scene.setArrowController(this);
target = Instantiate(TargetPrefabs);
}
// Update is called once per frame
void Update () {
ArrowFactory.getInstance().detectArrowsReuse();
}
public bool ifReadyToShoot()
{
return (holdingArrow != null);
}
internal void getArrow()
{
if (holdingArrow == null) holdingArrow = ArrowFactory.getInstance().getArrow();
}
internal void shootArrow(Vector3 mousePos,float xuli)
{
holdingArrow.transform.LookAt(mousePos * 30);
holdingArrow.GetComponent<Rigidbody>().isKinematic = false;
addWind(); //箭在射出过程中会持续受到一个风力
if(xuli <= 2)
{
holdingArrow.GetComponent<Rigidbody>().AddForce(mousePos * 30 * xuli, ForceMode.Impulse);
}
else
{
holdingArrow.GetComponent<Rigidbody>().AddForce(mousePos * 60, ForceMode.Impulse);
}
holdingArrow = null;
}
internal void changeArrowpos(Vector3 mousePos)
{
holdingArrow.transform.LookAt(mousePos * 30);
}
private void addWind()
{
int windDir = scene.getWindDirection();
int windStrength = scene.getWindStrength();
Vector3 windForce = winds[windDir]*50;
holdingArrow.GetComponent<Rigidbody>().AddForce(windForce, ForceMode.Force);
}
}
2.7 bodytremble
颤抖脚本,以原来位置为基础,进行7弧度的颤抖
public class bodytremble : MonoBehaviour
{
float radian = 0; // 弧度
float per_radian = 7f; // 每次变化的弧度
float radius = 0.01f; // 半径
Vector3 old_pos; // 开始时候的坐标
public float left_time = 8f; //动作持续时间
// Start is called before the first frame update
void Start()
{
old_pos = transform.position;
}
// Update is called once per frame
void Update()
{
left_time -= Time.deltaTime;
if (left_time <= 0)
{
//颤抖后回到初始位置
transform.position = old_pos;
}
// 弧度每次增加
radian += per_radian;
//y轴的位置变化,上下颤抖
float dy = Mathf.Cos(radian) * radius;
transform.position = old_pos + new Vector3(0, dy, 0);
Debug.Log("tremble");
}
}
三、实现效果
- 左上摄像机用于观察颤抖效果,右上摄像机用于观察中靶情况
- 动画演示:右键移动瞄准,左键按住蓄力,松开发射,这里设定了颤抖8秒,所以左上一直在抖,有点鬼畜