3D游戏编程与设计7——模型与动画

本文介绍了3D游戏编程中的智能巡逻兵设计,包括地图与巡逻路径设定,巡逻兵的障碍物避让和玩家追击行为。巡逻兵沿3-5边的凸多边形路径移动,采用订阅发布模式传递消息。游戏设计包含分数系统,当玩家甩掉巡逻兵或与之碰撞时触发相应事件。代码中涉及SSDirector、FirstController、PatrolFactory等关键类的实现。
1、智能巡逻兵
  • 提交要求:
  • 游戏设计要求:
    • 创建一个地图和若干巡逻兵(使用动画);
    • 每个巡逻兵走一个3~5个边的凸多边形,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    • 巡逻兵在设定范围内感知到玩家,则会自动追击玩家;
    • 失去玩家目标后,继续巡逻;
    • 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
  • 程序设计要求:
    • 必须使用订阅与发布模式传消息
      • subject:OnLostGoal
      • Publisher:?
      • Subscriber:?
    • 工厂模式生产巡逻兵
  • 友善提示1:生产3~5个边的凸多边形
    • 随机生成矩形
    • 在矩形每个边上随机找点,可得到3~4的凸多边形
    • 5?

代码说明:

  • SSDirector:用来管理游戏场景的导演类。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSDirector : System.Object
{

    private static SSDirector _instance;
    public ISceneController CurrentScenceController { get; set; }
    public static SSDirector GetInstance()
    {
        if (_instance == null)
        {
            _instance = new SSDirector();
        }
        return _instance;
    }
}
  • FirstController:游戏的第一个场景,用来加载资源。运用观察者模式,在游戏场景中订阅了分数以及游戏结束的事件,并在该类中实现具体事件。此外还提供了UserGUI可以使用的接口。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum GameState : int { GAMEOVER, RUNNING, RESTART }

public class FirstController : MonoBehaviour, ISceneController, IUserAction
{

    PatrolFactory df = null;
    GameState gamestate;
    private GameObject player;
    private int score;

    void Awake()
    {
        SSDirector director = SSDirector.GetInstance();
        director.CurrentScenceController = this;
        df = Singleton<PatrolFactory>.Instance;
        director.CurrentScenceController.LoadResources();
        gamestate = GameState.RUNNING;
        score = 0;

        this.gameObject.AddComponent<UserGUI>();
        this.gameObject.AddComponent<GameEventManager>();
    }

    public void LoadResources()
    {
        //产生迷宫
        GameObject maze = Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/maze"));
        maze.name = "maze";

        //产生玩家
        player = Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/FreeVoxelGirlBlackhairPrefab 1"));
        player.name = "player";
        player.transform.position = new Vector3(0, 0, 2);

        df.GetPatrol(new Vector3(-13, 0, 8), PatrolDirection.RIGHT);
        df.GetPatrol(new Vector3(3, 0, 8), PatrolDirection.DOWN);
        df.GetPatrol(new Vector3(13, 0, 2), PatrolDirection.LEFT);
        df.GetPatrol(new Vector3(-13, 0, -8), PatrolDirection.UP);
        df.GetPatrol(new Vector3(-3, 0, -2), PatrolDirection.RIGHT);
        df.GetPatrol(new Vector3(13, 0, -2), PatrolDirection.DOWN);
    }

    void OnEnable()
    {
        GameEventManager.OnScoreAction += addScore;
        GameEventManager.OnGameOver += SetGameOver;
    }

    void OnDisable()
    {
        GameEventManager.OnScoreAction -= addScore;
        GameEventManager.OnGameOver -= SetGameOver;
    }

    public GameState getGameState()
    {
        return gamestate;
    }

    public int getScore()
    {
        return score;
    }

    public void SetGameOver()
    {
        gamestate = GameState.GAMEOVER;
    }

    public void setGameState(GameState state)
    {
        gamestate = state;
    }

    public void restart()
    {
        player.transform.position = new Vector3(0, 0, 2);
        gamestate = GameState.RUNNING;
        df.RestartAll();

        score = -1;
    }

    public void addScore()
    {

        score++;
    }

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {

    }
}
  • IUserAction:场景的一个接口,其他类可以通过调用接口中的函数来与游戏场景进行交互。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface IUserAction
{

    GameState getGameState();
    int getScore();
    void setGameState(GameState state);
    void restart();
    void addScore();
}
  • ISceneController:场景的另一个接口,声明了场景应实现的功能。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ISceneController
{

    void LoadResources();
}
  • GameEventManager:观察者模式中的事件源,定义了所有事件触发的函数。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameEventManager : MonoBehaviour
{

    public delegate void ScoreAction();
    public static event ScoreAction OnScoreAction;

    public delegate void GameoverAction();
    public static event GameoverAction OnGameOver;

    public void PlayerEscape()
    {
        if (OnScoreAction != null)
        {
            OnScoreAction();
        }
    }

    public void GameOver()
    {
        if (OnGameOver != null)
        {
            OnGameOver();
        }
    }
}
  • Singleton:提供获取单例的方法。使用工厂,事件源等类时都会用到。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    protected static T instance;
    public static T Instance
    {
        get
        {
            if (instance == null)
            {
                instance = (T)FindObjectOfType(typeof(T));
                if (instance == null)
                {
                    Debug.LogError("An instance of " + typeof(T)
                        + " is needed in the scene, but there is none.");
                }
            }
            return instance;
        }
    }
}
  • PatrolFactory:管理巡逻兵的工厂。能产生巡逻兵和在游戏重新开始时将巡逻兵复原。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UserGUI : MonoBehaviour
{

    private IUserAction action;
    private int score;

    // Use this for initialization
    void Start()
    {
        action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
    }

    // Update is called once per frame
    void Update()
    {
        score = action.getScore();
    }

    void OnGUI()
    {
        GUI.Box(new Rect(Screen.width / 2 - 75, 10, 150, 55), "\nYour score:  " + score);
        if (action.getGameState() == GameState.GAMEOVER)
        {
            GUI.Window(0, new Rect(Screen.width / 2 - Screen.width / 12, Screen.height / 2 - Screen.height / 12, Screen.width / 6, Screen.height / 6), game_over_window, "Game Orver!");

        }

    }

    void game_over_window(int id)
    {
        if (GUI.Button(new Rect(Screen.width / 24, Screen.height / 24 + 5, Screen.width / 12, Screen.height / 12), "Restart"))
        {
            Debug.Log("Restart");

            action.restart();
        }
    }
}
  • PatrolData:管理巡逻兵信息以及动作的类。作为观察者,触发事件源。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum state : int { PATROL, CATCH }
public enum PatrolDirection : int { RIGHT, DOWN, LEFT, UP }

public class PatrolData : MonoBehaviour
{


    public state patrolState;
    public PatrolDirection direction;
    public PatrolDirection initDirection;
    public Vector3 target;
    public Vector3 initTarget;
    public float speed = 1f;
    public float catchSpeed = 2f;
    public GameObject player;

    private void Start()
    {
        patrolState = state.PATROL;
    }

    private void Update()
    {
        if (patrolState == state.PATROL)
        {
            this.transform.position = Vector3.MoveTowards(this.transform.position,
                target, speed * Time.deltaTime);
            if (transform.position == target)
            {
                direction = (PatrolDirection)(((int)direction + 1) % 4);
                if (direction == PatrolDirection.RIGHT)
                {
                    target = new Vector3(target.x + 6, target.y, target.z);
                }
                else if (direction == PatrolDirection.DOWN)
                {
                    target = new Vector3(target.x, target.y, target.z - 6);
                }
                else if (direction == PatrolDirection.UP)
                {
                    target = new Vector3(target.x, target.y, target.z + 6);
                }
                else if (direction == PatrolDirection.LEFT)
                {
                    target = new Vector3(target.x - 6, target.y, target.z);
                }
            }
        }
        else if (patrolState == state.CATCH)
        {
            this.transform.position = Vector3.MoveTowards(this.transform.position,
                player.transform.position, catchSpeed * Time.deltaTime);
            if (this.transform.position == player.transform.position)
            {
                Singleton<GameEventManager>.Instance.GameOver();
            }
        }
    }

    void OnTriggerEnter(Collider collider)
    {
        if (collider.gameObject.tag == "Player")
        {
            player = collider.gameObject;
            patrolState = state.CATCH;
        }
    }
    void OnTriggerExit(Collider collider)
    {
        if (collider.gameObject.tag == "Player")
        {
            patrolState = state.PATROL;

            Singleton<GameEventManager>.Instance.PlayerEscape();
        }
    }
}
  • joystick:定义玩家如何利用键盘操纵任务。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class joystick : MonoBehaviour
{

    public float speedX = 5f;
    public float speedY = 5f;
    public float rotate_speed = 1500f;

    // Use this for initialization
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        float translationY = Input.GetAxis("Vertical") * speedY;
        float translationX = Input.GetAxis("Horizontal") * speedX;
        translationX *= Time.deltaTime;
        translationY *= Time.deltaTime;
        transform.Translate(translationX, 0, translationY);
        transform.Rotate(0, translationX * rotate_speed * Time.deltaTime, 0);
        if (transform.localEulerAngles.x != 0 || transform.localEulerAngles.z != 0)
        {
            transform.localEulerAngles = new Vector3(0, transform.localEulerAngles.y, 0);
        }
        if (transform.position.y != 0)
        {
            transform.position = new Vector3(transform.position.x, 0, transform.position.z);
        }
    }
}
Delphi 12.3 作为一款面向 Windows 平台的集成开发环境,由 Embarcadero Technologies 负责其持续演进。该环境以 Object Pascal 语言为核心,并依托 Visual Component Library(VCL)框架,广泛应用于各类桌面软件、数据库系统及企业级解决方案的开发。在此生态中,Excel4Delphi 作为一个重要的社区开源项目,致力于搭建 Delphi Microsoft Excel 之间的高效桥梁,使开发者能够在自研程序中直接调用 Excel 的文档处理、工作表管理、单元格操作及宏执行等功能。 该项目以库文件组件包的形式提供,开发者将其集成至 Delphi 工程后,即可通过封装良好的接口实现对 Excel 的编程控制。具体功能涵盖创建编辑工作簿、格式化单元格、批量导入导出数据,乃至执行内置公式宏指令等高级操作。这一机制显著降低了在财务分析、报表自动生成、数据整理等场景中实现 Excel 功能集成的技术门槛,使开发者无需深入掌握 COM 编程或 Excel 底层 API 即可完成复杂任务。 使用 Excel4Delphi 需具备基础的 Delphi 编程知识,并对 Excel 对象模型有一定理解。实践中需注意不同 Excel 版本间的兼容性,并严格遵循项目文档进行环境配置依赖部署。此外,操作过程中应遵循文件访问的最佳实践,例如确保目标文件未被独占锁定,并实施完整的异常处理机制,以防数据损毁或程序意外中断。 该项目的持续维护依赖于 Delphi 开发者社区的集体贡献,通过定期更新以适配新版开发环境 Office 套件,并修复已发现的问题。对于需要深度融合 Excel 功能的 Delphi 应用而言,Excel4Delphi 提供了经过充分测试的可靠代码基础,使开发团队能更专注于业务逻辑用户体验的优化,从而提升整体开发效率软件质量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值