精华内容
下载资源
问答
  • 在保安工作中,客观及时地...针对一个已经研制成功的基于IC卡技术的区域巡逻监察系统,探讨了该类系统的主要作用、系统的总体设计思路和目标、系统实施的技术要点以及系统的特色,同时也讨论了系统的应用领域和前景。
  • 针对警车的配置和巡逻区域覆盖问题,通过引入k-means聚类算法、最小顶点覆盖和遗传算法等,提出一种警车优化配置和全局最优的巡逻区域最大覆盖调度方案。利用k-means聚类算法生成的Ⅳ个中心点作为警车初始位置的参考...
  • 服务/行动对比从上面可以非常明显的看出,服务和行动的差异。 那么实践任务如下: 用行动实现第8讲中,第三种服务的功能,单目标点多参数; 行动实践1用行动实现mobot在室内环境各房间的巡逻,多目标点多参数; ...

    行动(action)比服务更为灵活和复杂。在给出行动具体说明之前,先简要复习一下:

    主题-服务-行动:

    场合

     

    具体细节

     

    服务/行动对比

    从上面可以非常明显的看出,服务和行动的差异。

    自动驾驶mobot

    那么实践任务如下:

    • 用行动实现第8讲中,第三种服务的功能,单目标点多参数;
    行动实践1
    • 用行动实现mobot在室内环境各房间的巡逻,多目标点多参数;
    行动实践2
    • 用行动实现mobot在室外跑道的巡逻,多目标点多参数不确定。
    行动实践3-日光
    行动实践3-灯光

    由此,需要融合OpenAI,OpenCV和BT。从机器人主题,服务过渡到行为。

    然而行为的组合,不同状态行为的切换,构成了新的挑战,进一步学习:

    熟练掌握相应算法进一步可扩展:

    • 物流机器人(点点轨迹,任务调度)
    • 清扫机器人(区域覆盖,协作协同)

    行为(action)基础复习:

    斐波那契数列与黄金分割

    行为服务器端:

    • 非推荐,ros1代码风格
    #include <inttypes.h>
    #include <memory>
    #include "example_interfaces/action/fibonacci.hpp"
    #include "rclcpp/rclcpp.hpp"
    // TODO(jacobperron): Remove this once it is included as part of 'rclcpp.hpp'
    #include "rclcpp_action/rclcpp_action.hpp"
    
    using Fibonacci = example_interfaces::action::Fibonacci;
    using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>;
    
    rclcpp_action::GoalResponse handle_goal(
      const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const Fibonacci::Goal> goal)
    {
      RCLCPP_INFO(rclcpp::get_logger("server"), "Got goal request with order %d", goal->order);
      (void)uuid;
      // Let's reject sequences that are over 9000
      if (goal->order > 9000) {
        return rclcpp_action::GoalResponse::REJECT;
      }
      return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
    }
    
    rclcpp_action::CancelResponse handle_cancel(
      const std::shared_ptr<GoalHandleFibonacci> goal_handle)
    {
      RCLCPP_INFO(rclcpp::get_logger("server"), "Got request to cancel goal");
      (void)goal_handle;
      return rclcpp_action::CancelResponse::ACCEPT;
    }
    
    void execute(
      const std::shared_ptr<GoalHandleFibonacci> goal_handle)
    {
      RCLCPP_INFO(rclcpp::get_logger("server"), "Executing goal");
      rclcpp::Rate loop_rate(1);
      const auto goal = goal_handle->get_goal();
      auto feedback = std::make_shared<Fibonacci::Feedback>();
      auto & sequence = feedback->sequence;
      sequence.push_back(0);
      sequence.push_back(1);
      auto result = std::make_shared<Fibonacci::Result>();
    
      for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) {
        // Check if there is a cancel request
        if (goal_handle->is_canceling()) {
          result->sequence = sequence;
          goal_handle->canceled(result);
          RCLCPP_INFO(rclcpp::get_logger("server"), "Goal Canceled");
          return;
        }
        // Update sequence
        sequence.push_back(sequence[i] + sequence[i - 1]);
        // Publish feedback
        goal_handle->publish_feedback(feedback);
        RCLCPP_INFO(rclcpp::get_logger("server"), "Publish Feedback");
    
        loop_rate.sleep();
      }
    
      // Check if goal is done
      if (rclcpp::ok()) {
        result->sequence = sequence;
        goal_handle->succeed(result);
        RCLCPP_INFO(rclcpp::get_logger("server"), "Goal Succeeded");
      }
    }
    
    void handle_accepted(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
    {
      // this needs to return quickly to avoid blocking the executor, so spin up a new thread
      std::thread{execute, goal_handle}.detach();
    }
    
    int main(int argc, char ** argv)
    {
      rclcpp::init(argc, argv);
      auto node = rclcpp::Node::make_shared("minimal_action_server");
    
      // Create an action server with three callbacks
      //   'handle_goal' and 'handle_cancel' are called by the Executor (rclcpp::spin)
      //   'execute' is called whenever 'handle_goal' returns by accepting a goal
      //    Calls to 'execute' are made in an available thread from a pool of four.
      auto action_server = rclcpp_action::create_server<Fibonacci>(
        node,
        "fibonacci",
        handle_goal,
        handle_cancel,
        handle_accepted);
    
      rclcpp::spin(node);
    
      rclcpp::shutdown();
      return 0;
    }
    • 推荐,ros2新风格
    #include <inttypes.h>
    #include <memory>
    #include "example_interfaces/action/fibonacci.hpp"
    #include "rclcpp/rclcpp.hpp"
    // TODO(jacobperron): Remove this once it is included as part of 'rclcpp.hpp'
    #include "rclcpp_action/rclcpp_action.hpp"
    
    class MinimalActionServer : public rclcpp::Node
    {
    public:
      using Fibonacci = example_interfaces::action::Fibonacci;
      using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>;
    
      explicit MinimalActionServer(const rclcpp::NodeOptions & options = rclcpp::NodeOptions())
      : Node("minimal_action_server", options)
      {
        using namespace std::placeholders;
    
        this->action_server_ = rclcpp_action::create_server<Fibonacci>(
          this->get_node_base_interface(),
          this->get_node_clock_interface(),
          this->get_node_logging_interface(),
          this->get_node_waitables_interface(),
          "fibonacci",
          std::bind(&MinimalActionServer::handle_goal, this, _1, _2),
          std::bind(&MinimalActionServer::handle_cancel, this, _1),
          std::bind(&MinimalActionServer::handle_accepted, this, _1));
      }
    
    private:
      rclcpp_action::Server<Fibonacci>::SharedPtr action_server_;
    
      rclcpp_action::GoalResponse handle_goal(
        const rclcpp_action::GoalUUID & uuid,
        std::shared_ptr<const Fibonacci::Goal> goal)
      {
        RCLCPP_INFO(this->get_logger(), "Received goal request with order %d", goal->order);
        (void)uuid;
        // Let's reject sequences that are over 9000
        if (goal->order > 9000) {
          return rclcpp_action::GoalResponse::REJECT;
        }
        return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
      }
    
      rclcpp_action::CancelResponse handle_cancel(
        const std::shared_ptr<GoalHandleFibonacci> goal_handle)
      {
        RCLCPP_INFO(this->get_logger(), "Received request to cancel goal");
        (void)goal_handle;
        return rclcpp_action::CancelResponse::ACCEPT;
      }
    
      void execute(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
      {
        RCLCPP_INFO(this->get_logger(), "Executing goal");
        rclcpp::Rate loop_rate(1);
        const auto goal = goal_handle->get_goal();
        auto feedback = std::make_shared<Fibonacci::Feedback>();
        auto & sequence = feedback->sequence;
        sequence.push_back(0);
        sequence.push_back(1);
        auto result = std::make_shared<Fibonacci::Result>();
    
        for (int i = 1; (i < goal->order) && rclcpp::ok(); ++i) {
          // Check if there is a cancel request
          if (goal_handle->is_canceling()) {
            result->sequence = sequence;
            goal_handle->canceled(result);
            RCLCPP_INFO(this->get_logger(), "Goal Canceled");
            return;
          }
          // Update sequence
          sequence.push_back(sequence[i] + sequence[i - 1]);
          // Publish feedback
          goal_handle->publish_feedback(feedback);
          RCLCPP_INFO(this->get_logger(), "Publish Feedback");
    
          loop_rate.sleep();
        }
    
        // Check if goal is done
        if (rclcpp::ok()) {
          result->sequence = sequence;
          goal_handle->succeed(result);
          RCLCPP_INFO(this->get_logger(), "Goal Suceeded");
        }
      }
    
      void handle_accepted(const std::shared_ptr<GoalHandleFibonacci> goal_handle)
      {
        using namespace std::placeholders;
        // this needs to return quickly to avoid blocking the executor, so spin up a new thread
        std::thread{std::bind(&MinimalActionServer::execute, this, _1), goal_handle}.detach();
      }
    };  // class MinimalActionServer
    
    int main(int argc, char ** argv)
    {
      rclcpp::init(argc, argv);
    
      auto action_server = std::make_shared<MinimalActionServer>();
    
      rclcpp::spin(action_server);
    
      rclcpp::shutdown();
      return 0;
    }

    行为客户端:

    • 非推荐,ros1风格
    #include <inttypes.h>
    #include <memory>
    #include "example_interfaces/action/fibonacci.hpp"
    #include "rclcpp/rclcpp.hpp"
    // TODO(jacobperron): Remove this once it is included as part of 'rclcpp.hpp'
    #include "rclcpp_action/rclcpp_action.hpp"
    
    using Fibonacci = example_interfaces::action::Fibonacci;
    
    
    int main(int argc, char ** argv)
    {
      rclcpp::init(argc, argv);
      auto node = rclcpp::Node::make_shared("minimal_action_client");
      auto action_client = rclcpp_action::create_client<Fibonacci>(node, "fibonacci");
    
      if (!action_client->wait_for_action_server(std::chrono::seconds(20))) {
        RCLCPP_ERROR(node->get_logger(), "Action server not available after waiting");
        return 1;
      }
    
      // Populate a goal
      auto goal_msg = Fibonacci::Goal();
      goal_msg.order = 10;
    
      RCLCPP_INFO(node->get_logger(), "Sending goal");
      // Ask server to achieve some goal and wait until it's accepted
      auto goal_handle_future = action_client->async_send_goal(goal_msg);
      if (rclcpp::spin_until_future_complete(node, goal_handle_future) !=
        rclcpp::executor::FutureReturnCode::SUCCESS)
      {
        RCLCPP_ERROR(node->get_logger(), "send goal call failed :(");
        return 1;
      }
    
      rclcpp_action::ClientGoalHandle<Fibonacci>::SharedPtr goal_handle = goal_handle_future.get();
      if (!goal_handle) {
        RCLCPP_ERROR(node->get_logger(), "Goal was rejected by server");
        return 1;
      }
    
      // Wait for the server to be done with the goal
      auto result_future = goal_handle->async_result();
    
      RCLCPP_INFO(node->get_logger(), "Waiting for result");
      if (rclcpp::spin_until_future_complete(node, result_future) !=
        rclcpp::executor::FutureReturnCode::SUCCESS)
      {
        RCLCPP_ERROR(node->get_logger(), "get result call failed :(");
        return 1;
      }
    
      rclcpp_action::ClientGoalHandle<Fibonacci>::WrappedResult wrapped_result = result_future.get();
    
      switch (wrapped_result.code) {
        case rclcpp_action::ResultCode::SUCCEEDED:
          break;
        case rclcpp_action::ResultCode::ABORTED:
          RCLCPP_ERROR(node->get_logger(), "Goal was aborted");
          return 1;
        case rclcpp_action::ResultCode::CANCELED:
          RCLCPP_ERROR(node->get_logger(), "Goal was canceled");
          return 1;
        default:
          RCLCPP_ERROR(node->get_logger(), "Unknown result code");
          return 1;
      }
    
      RCLCPP_INFO(node->get_logger(), "result received");
      for (auto number : wrapped_result.result->sequence) {
        RCLCPP_INFO(node->get_logger(), "%" PRId64, number);
      }
    
    • 推荐:ros2风格
    #include <inttypes.h>
    #include <memory>
    #include <string>
    #include <iostream>
    #include "example_interfaces/action/fibonacci.hpp"
    #include "rclcpp/rclcpp.hpp"
    // TODO(jacobperron): Remove this once it is included as part of 'rclcpp.hpp'
    #include "rclcpp_action/rclcpp_action.hpp"
    
    class MinimalActionClient : public rclcpp::Node
    {
    public:
      using Fibonacci = example_interfaces::action::Fibonacci;
      using GoalHandleFibonacci = rclcpp_action::ClientGoalHandle<Fibonacci>;
    
      explicit MinimalActionClient(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
      : Node("minimal_action_client", node_options), goal_done_(false)
      {
        this->client_ptr_ = rclcpp_action::create_client<Fibonacci>(
          this->get_node_base_interface(),
          this->get_node_graph_interface(),
          this->get_node_logging_interface(),
          this->get_node_waitables_interface(),
          "fibonacci");
    
        this->timer_ = this->create_wall_timer(
          std::chrono::milliseconds(500),
          std::bind(&MinimalActionClient::send_goal, this));
      }
    
      bool is_goal_done() const
      {
        return this->goal_done_;
      }
    
      void send_goal()
      {
        using namespace std::placeholders;
    
        this->timer_->cancel();
    
        this->goal_done_ = false;
    
        if (!this->client_ptr_) {
          RCLCPP_ERROR(this->get_logger(), "Action client not initialized");
        }
    
        if (!this->client_ptr_->wait_for_action_server(std::chrono::seconds(10))) {
          RCLCPP_ERROR(this->get_logger(), "Action server not available after waiting");
          this->goal_done_ = true;
          return;
        }
    
        auto goal_msg = Fibonacci::Goal();
        goal_msg.order = 10;
    
        RCLCPP_INFO(this->get_logger(), "Sending goal");
    
        auto send_goal_options = rclcpp_action::Client<Fibonacci>::SendGoalOptions();
        send_goal_options.goal_response_callback =
          std::bind(&MinimalActionClient::goal_response_callback, this, _1);
        send_goal_options.feedback_callback =
          std::bind(&MinimalActionClient::feedback_callback, this, _1, _2);
        send_goal_options.result_callback =
          std::bind(&MinimalActionClient::result_callback, this, _1);
        auto goal_handle_future = this->client_ptr_->async_send_goal(goal_msg, send_goal_options);
      }
    
    private:
      rclcpp_action::Client<Fibonacci>::SharedPtr client_ptr_;
      rclcpp::TimerBase::SharedPtr timer_;
      bool goal_done_;
    
      void goal_response_callback(std::shared_future<GoalHandleFibonacci::SharedPtr> future)
      {
        auto goal_handle = future.get();
        if (!goal_handle) {
          RCLCPP_ERROR(this->get_logger(), "Goal was rejected by server");
        } else {
          RCLCPP_INFO(this->get_logger(), "Goal accepted by server, waiting for result");
        }
      }
    
      void feedback_callback(
        GoalHandleFibonacci::SharedPtr,
        const std::shared_ptr<const Fibonacci::Feedback> feedback)
      {
        RCLCPP_INFO(
          this->get_logger(),
          "Next number in sequence received: %" PRId64,
          feedback->sequence.back());
      }
    
      void result_callback(const GoalHandleFibonacci::WrappedResult & result)
      {
        this->goal_done_ = true;
        switch (result.code) {
          case rclcpp_action::ResultCode::SUCCEEDED:
            break;
          case rclcpp_action::ResultCode::ABORTED:
            RCLCPP_ERROR(this->get_logger(), "Goal was aborted");
            return;
          case rclcpp_action::ResultCode::CANCELED:
            RCLCPP_ERROR(this->get_logger(), "Goal was canceled");
            return;
          default:
            RCLCPP_ERROR(this->get_logger(), "Unknown result code");
            return;
        }
    
        RCLCPP_INFO(this->get_logger(), "Result received");
        for (auto number : result.result->sequence) {
          RCLCPP_INFO(this->get_logger(), "%" PRId64, number);
        }
      }
    };  // class MinimalActionClient
    
    int main(int argc, char ** argv)
    {
      rclcpp::init(argc, argv);
      auto action_client = std::make_shared<MinimalActionClient>();
    
      while (!action_client->is_goal_done()) {
        rclcpp::spin_some(action_client);
      }
    
      rclcpp::shutdown();
      return 0;
    }

     

     

    展开全文
  • unity3D 巡逻

    2018-05-11 21:10:08
    - 结构UML 游戏介绍 1.该游戏存在6个区域(1~6),每个区域都有一个巡逻兵,各区域用围墙分开,相邻区域的围墙有间隙可以通过,角色不可穿墙。游戏角色初始化在时在区域5。...各区域巡逻兵...

    这里写图片描述
    - 结构UML
    这里写图片描述

    • 游戏介绍
      这里写图片描述
      这里写图片描述

      1.该游戏存在6个区域(1~6),每个区域都有一个巡逻兵,各区域用围墙分开,相邻区域的围墙有间隙可以通过,角色不可穿墙。游戏角色初始化在时在区域5。
      2.当玩家进入某一区域时,该区域内的巡逻兵会自动追击玩家,直至玩家离开该区域。若玩家被巡逻兵抓到,游戏结束。当巡逻兵所在区域没有其他角色的时候巡逻兵沿着四边形运动。
      3.各区域巡逻兵不会离开自己所在区域,当玩家躲过一个巡逻兵的追击时,即安全的通过一个区域时,分数会+1。
      4.每得5分,游戏等级提升一次,升级巡逻兵的速度。

    • 关键 模式


      • 工厂模式

      在游戏中,使用巡逻兵工厂来产生巡逻兵,当兵工厂中有巡逻兵时,重设参数并将该巡逻兵交给需求方,当兵工厂中没有巡逻兵的时候,实例化一个新的巡逻兵并初始化,返回给需求方。

            public GameObject GetSoldier()
            {
                if (free.Count != 0)
                {
                    used.Add(free[0]);
                    free.RemoveAt(0);
                    used[used.Count - 1].SetActive(true);
                    used[used.Count - 1].GetComponent<MeshRenderer>().material.color = Color.black;
                }
                else
                {
                    GameObject tempDisk = Instantiate(Resources.Load("Prefabs/Soldier"), Vector3.up, Quaternion.identity) as GameObject;
                    used.Add(tempDisk);
                    used[used.Count - 1].GetComponent<MeshRenderer>().material.color = Color.black;
                    used[used.Count - 1].SetActive(true);
                }
                return used[used.Count - 1];
            }


    • 发布-订阅模式

    使用玩家作为消息发布方,将自己的相关信息(如:生存状态,位置,所在区域)发送给订阅者,订阅者根据自己的需求,从所有信息中得到自己想要的部分(如:场景控制器判断游戏是否结束,巡逻兵判断是否在自己所在区域,是否追击),并根据信息做下一步动作。
    该游戏中,发布对象为玩家,发布信息有:存活状态,位置,所在区域。订阅者有巡逻兵:根据玩家所在区域判断是否追击、根据玩家状态判断是否追击、根据玩家位置判断追击方向;场景控制器:根据玩家状态判断游戏是否结束;记分员:根据玩家所在区域判断是否得分。

    //Observer
        public abstract class Observer : MonoBehaviour
        {
            public abstract void Reaction(bool aLive, Vector3 playPosition, int playLocation);
    
    //Subject
        public abstract class Subject : MonoBehaviour
        {
            List<Observer> m_Observers = new List<Observer>();
    
            public abstract void Attach(Observer listener);
    
            public abstract void Detach(Observer theObserver);
    
            public abstract void Notify(bool aLive, Vector3 playPosition, int playLocation);
        }
        }
    
    //Player<Publish>
            protected List<Observer> obs = new List<Observer>();   //所有观察者
            public override void Attach(Observer o)
            {
                obs.Add(o);
            }
    
            public override void Detach(Observer o)
            {
                obs.Remove(o);
            }
    
            public override void Notify(bool aLive, Vector3 playPosition, int playLocation)
            {
                foreach (Observer o in obs)
                {
                    o.Reaction(aLive, playPosition, playLocation);
                }
            }
    
    //SceneController<Subscribe>
            public override void Reaction(bool aLive, Vector3 playPosition, int playLocation)
            {
                isOver = !aLive;
            }
    
    //ScoreManager<Subscribe>
            public override void Reaction(bool aLive, Vector3 playPosition, int playLocation)
            {
                playerIsLive = aLive;
                currentLoc = playLocation;
                if (currentLoc != lastLoc)
                {
                    score++;
                    lastLoc = currentLoc;
                }
            }
    
    //Soldier<Subscribe>
            public override void Reaction (bool _aLive, Vector3 _playPosition, int _playLocation)
            {
                playerLocation = _playLocation;
                playerPosition = _playPosition;
                playAlive = _aLive;
            }
    • 实现及关键代码


      - 角色Player

      Player的运动通过从键盘上读取输入来实现。在角色的编写中,会遇到一个问题:当碰撞到墙壁时,由于输入由玩家控制,所以可能导致碰撞挤压导致旋转,旋转后,再运动可能会“飞上天”,因此,每次运动的时候,通过显式修改transform.rotation的位置,使得x z的值始终为0。同时,角色的消息发布是在Update()中进行的,但是由于发生碰撞事件之后,该角色的变为不活跃,不再执行该函数,所以在发生碰撞后同样要执行一次Notify()
                //move
                transform.Translate(Vector3.forward * speed * Input.GetAxisRaw("Horizontal") * Time.deltaTime);
                transform.Translate(Vector3.left * speed * Input.GetAxisRaw("Vertical") * Time.deltaTime);
                transform.Rotate(Vector3.up, Input.GetAxisRaw("Horizontal") * angle_speed * Time.deltaTime);
                //set rotation to make player at floor
                float y = transform.rotation.y;
                transform.rotation = Quaternion.Euler(0, y, 0);
    
            private void OnCollisionEnter(Collision c)
            {
                if (c.gameObject.CompareTag("Soldier"))
                {
                    isLive = false;
                    Notify(isLive, transform.position, loc);
                }
            }
    
    • 角色Soldier与角色工厂SoldierFactory

    角色运动的第一个问题是判断路径,在得到玩家发布的信息后,会先判断玩家的所在区域,如果是自己的所在区域,就向玩家方向追击。当巡逻时,会先想着当前前进方向前进,运动到一定时间的时候,会转向90度(因此正常运行会走四边形路线),当发生碰撞的时候,如果和墙发生了碰撞,就转向。

            void catchPlayer()
            {
                catching = true;
                transform.LookAt(playerPosition);
                transform.position = Vector3.Lerp(transform.position, playerPosition, catchSpeed * Time.deltaTime);
            }
    
            void move ()
            {
                transform.Translate(0, 0, walkSpeed * Time.deltaTime);
            }
            update () {
            .......
                if (index == playerLocation)
                {
                    ani.SetBool("catching", true);
                    catchPlayer();
                }
                else
                {
                    ani.SetBool("catching", false);
                    move();
                    time += Time.deltaTime;
                }
                if (time >= 3f)
                {
                    turn();
                }
            }

    第二个问题是巡逻兵只能够在自己所在方向运动,那么碰撞到墙可以转向,如果遇到空隙了呢?此时不会发生碰撞,因此,编写了函数checkTurn(),此函数通过调用getNowLocation()来判断巡逻兵此时的位置,当此时的位置与自己的编号不同的时候,就需要转向。checkTurn()Update()中每一帧调用。
    在这里,使用了动画状态机,游戏角色有两个动作,分别为walkrun,对应巡逻与追赶。

            void checkTurn ()
            {
                if (index != getNowLocation())
                {
                    turn();
                }
            }
    
            int getNowLocation()
            {
                if (gameObject.transform.position.z > 5 && gameObject.transform.position.z < 15)
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 3;
                    }
                    else
                    {
                        return 6;
                    }
                }
                else if (gameObject.transform.position.z > -5 && gameObject.transform.position.z < 5)
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 2;
                    }
                    else
                    {
                        return 5;
                    }
                }
                else
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 1;
                    }
                    else
                    {
                        return 4;
                    }
                }
            }

    SoldierFactory实现使用了List<Soldier> usedList<Soldier> free,实现方式已在设计模式中给出,不再赘述。

    • Data数据源

    此模块用来生成场景所需要的所有对象。有两个关键问题,第一个问题是,玩家的声明在这里,但是需要订阅的ScoreManagerSceneController在他的上层,在这里用了一个不规范的方法,getObserver,通过将对象传进来,然后再创建。第二个问题是,如何获得巡逻兵的编号及初始位置。在这里,使用字段Soldier + 当前士兵数目作为name,赋给巡逻兵,并且创建了位置数组,将6个初始位置通过在地图上的位置得到。

        public class Data : MonoBehaviour
        {
            private Subject sub;
            private GameObject player;
            private Observer obs;
            private SoldierFactory soldierFac;
            private int soldierNum;
            private List<GameObject> soldierSet = new List<GameObject>();
            private Vector3[] soldierPos = new Vector3[] {new Vector3(-7.5f, -0.5f, -10), new Vector3(-7.5f, -0.5f, 0), new Vector3(-7.5f, -0.5f, 10),
                new Vector3(7.5f, -0.5f, -10), new Vector3(7.5f, -0.5f, 0), new Vector3(7.5f, -0.5f, 10)};
    
            void Start()
            {
                soldierNum = 0;
                soldierFac = gameObject.AddComponent<SoldierFactory>() as SoldierFactory;
                Instantiate(Resources.Load("Prefabs/World"), new Vector3 (0, 0f, 0), Quaternion.identity);
                player = Instantiate(Resources.Load("Prefabs/Player"), new Vector3(1, -0.5f, 1), Quaternion.identity) as GameObject;
                sub = player.GetComponent<Player>();
    
                for (int i = 0; i < 6; i++)
                {
                    GameObject soldier = soldierFac.GetSoldier();
                    soldier.name = "Soldier" + soldierNum;
                    soldierNum++;
                    obs = soldier.GetComponent<Soldier>();
                    sub.Attach(obs);
                    soldier.transform.position = soldierPos[i];
                }
            }
    
            public void getObserver (SceneController obs)
            {
                Subject tempSub = player.GetComponent<Player>();
                tempSub.Attach(obs);
            }
    
            public void getObserver(ScoreManager obs)
            {
                Subject tempSub = player.GetComponent<Player>();
                tempSub.Attach(obs);
            }
        } 
    • ScoreManager记分员

    比较简单,主要就是如何计分。由于订阅了玩家,因此,根据玩家的所在区域来计分,使用一个变量lastLoc来记录玩家上一次所在的区域,当得到的新的信息中,玩家的位置发生了改变,那么说明玩家已经安全的通过了上一区域,此时更新lastLoc并得分。

            public override void Reaction(bool aLive, Vector3 playPosition, int playLocation)
            {
                playerIsLive = aLive;
                currentLoc = playLocation;
                if (currentLoc != lastLoc)
                {
                    score++;
                    lastLoc = currentLoc;
                }
            }
    • SceneController场景控制器与UIControllerUI控制器

    此场景控制器包括了一个UIController(显示分数),一个数据源Data。较为简单。UIController包括一个ScoreManager,可以直接看代码。

    • 完整代码

      - Observer
        public abstract class Observer : MonoBehaviour
        {
            public abstract void Reaction(bool aLive, Vector3 playPosition, int playLocation);
    
        }
    • Subject
        public abstract class Subject : MonoBehaviour
        {
            List<Observer> m_Observers = new List<Observer>();
    
            public abstract void Attach(Observer listener);
    
            public abstract void Detach(Observer theObserver);
    
            public abstract void Notify(bool aLive, Vector3 playPosition, int playLocation);
        }
    • Player
        public class Player : Subject
        {
            int loc;
            private float speed;
            private float angle_speed;
            private bool isLive;
            private Vector3 position;
    
            // Use this for initialization
            void Start()
            {
                speed = 5f;
                angle_speed = 3f;
                isLive = true;
            }
            protected List<Observer> obs = new List<Observer>();   //所有观察者
    
            int getNowLocation()
            {
                if (gameObject.transform.position.z > 5 && gameObject.transform.position.z < 15)
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 3;
                    }
                    else
                    {
                        return 6;
                    }
                }
                else if (gameObject.transform.position.z > -5 && gameObject.transform.position.z < 5)
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 2;
                    }
                    else
                    {
                        return 5;
                    }
                }
                else
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 1;
                    }
                    else
                    {
                        return 4;
                    }
                }
            }
    
            public override void Attach(Observer o)
            {
                obs.Add(o);
            }
    
            public override void Detach(Observer o)
            {
                obs.Remove(o);
            }
    
            public override void Notify(bool aLive, Vector3 playPosition, int playLocation)
            {
                foreach (Observer o in obs)
                {
                    o.Reaction(isLive, playPosition, playLocation);
                }
            }
            private void Update()
            {
                //move
                transform.Translate(Vector3.forward * speed * Input.GetAxisRaw("Vertical") * Time.deltaTime);
                transform.Translate(Vector3.right * speed * Input.GetAxisRaw("Horizontal") * Time.deltaTime);
                transform.Rotate(Vector3.up, Input.GetAxisRaw("Vertical") * angle_speed * Time.deltaTime);
                //set rotation to make player at floor
                float y = transform.rotation.y;
                transform.rotation = Quaternion.Euler(0, y, 0);
                float x = transform.position.x;
                float z = transform.position.z;
                transform.position = new Vector3(x, -0.5f, z);
                loc = getNowLocation();
                Notify(isLive, transform.position, loc);
            }
    
            private void OnCollisionEnter(Collision c)
            {
                if (c.gameObject.CompareTag("Soldier"))
                {
                    isLive = false;
                    Notify(isLive, transform.position, loc);
                }
            }
        }
    • Soldier
        public class Soldier : Observer
        {
            private Animator ani;
            private int index;
            private bool catching;
            private int playerLocation;
            private Vector3 playerPosition;
            private float walkSpeed;
            private float catchSpeed;
            private bool playAlive;
            private float time;
    
    
            // Use this for initialization
            void Start()
            {
                ani = GetComponent<Animator>();
                index = getOwnIndex();
                walkSpeed = 0.25f;
                catchSpeed = 0.27f;
            }
    
            private int getOwnIndex()
            {
                string name = this.gameObject.name;
                char tempIndex = name[name.Length - 1];
                int result = tempIndex - '0';
                return result + 1;
            }
    
    
            public override void Reaction (bool _aLive, Vector3 _playPosition, int _playLocation)
            {
                playerLocation = _playLocation;
                playerPosition = _playPosition;
                playAlive = _aLive;
            }
    
            void catchPlayer()
            {
                catching = true;
                transform.LookAt(playerPosition);
                transform.position = Vector3.Lerp(transform.position, playerPosition, catchSpeed * Time.deltaTime);
            }
    
            void move ()
            {
                transform.Translate(0, 0, walkSpeed * Time.deltaTime);
            }
    
            void checkTurn ()
            {
                if (index != getNowLocation())
                {
                    turn();
                }
            }
    
            void turn ()
            {
                transform.Rotate(0, 90, 0);
                time = 0;
            }
    
            // Update is called once per frame
            void Update()
            {
                checkTurn();
                if (index == playerLocation)
                {
                    ani.SetBool("catching", true);
                    catchPlayer();
                }
                else
                {
                    ani.SetBool("catching", false);
                    move();
                    time += Time.deltaTime;
                }
                if (time >= 3f)
                {
                    turn();
                }
            }
    
            void OnCollisionEnter(Collision e)
            {
                //撞击围栏,选择下一个点移动  
                if (e.gameObject.CompareTag("Wall"))
                {
                    turn();
                }
    
                //撞击hero,游戏结束  
                if (e.gameObject.CompareTag("Player"))
                {
                    e.gameObject.SetActive(false);
                    catching = false;
                }
            }
    
            public void speedUp ()
            {
                catchSpeed += 0.1f;
            }
    
            int getNowLocation()
            {
                if (gameObject.transform.position.z > 5 && gameObject.transform.position.z < 15)
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 3;
                    }
                    else
                    {
                        return 6;
                    }
                }
                else if (gameObject.transform.position.z > -5 && gameObject.transform.position.z < 5)
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 2;
                    }
                    else
                    {
                        return 5;
                    }
                }
                else
                {
                    if (gameObject.transform.position.x < 0)
                    {
                        return 1;
                    }
                    else
                    {
                        return 4;
                    }
                }
            }
        }
    • SoldierFactory
        public class SoldierFactory : MonoBehaviour
        {
            public GameObject soldierPrefab;
            private static List<GameObject> used = new List<GameObject>();
            private static List<GameObject> free = new List<GameObject>();
    
            void Start()
            {
            }
    
            public GameObject GetSoldier()
            {
                if (free.Count != 0)
                {
                    used.Add(free[0]);
                    free.RemoveAt(0);
                    used[used.Count - 1].SetActive(true);
                }
                else
                {
                    GameObject tempDisk = Instantiate(Resources.Load("Prefabs/Soldier"), Vector3.up, Quaternion.identity) as GameObject;
                    used.Add(tempDisk);
                    used[used.Count - 1].SetActive(true);
                }
                return used[used.Count - 1];
            }
    
            public void FreeSoldier(GameObject soldier)
            {
                soldier.SetActive(false);
                used.Remove(soldier);
                free.Add(soldier);
            }
        }
    • Data
        public class Data : MonoBehaviour
        {
            private Subject sub;
            private GameObject player;
            private Observer obs;
            private SoldierFactory soldierFac;
            private int soldierNum;
            private List<GameObject> soldierSet = new List<GameObject>();
            private Vector3[] soldierPos = new Vector3[] {new Vector3(-7.5f, -0.5f, -10), new Vector3(-7.5f, -0.5f, 0), new Vector3(-7.5f, -0.5f, 10),
                new Vector3(7.5f, -0.5f, -10), new Vector3(7.5f, -0.5f, 0), new Vector3(7.5f, -0.5f, 10)};
    
            void Start()
            {
                soldierNum = 0;
                soldierFac = gameObject.AddComponent<SoldierFactory>() as SoldierFactory;
                Instantiate(Resources.Load("Prefabs/World"), new Vector3 (0, 0f, 0), Quaternion.identity);
                player = Instantiate(Resources.Load("Prefabs/Player"), new Vector3(1, -0.5f, 1), Quaternion.identity) as GameObject;
                sub = player.GetComponent<Player>();
    
                for (int i = 0; i < 6; i++)
                {
                    GameObject soldier = soldierFac.GetSoldier();
                    soldier.name = "Soldier" + soldierNum;
                    soldierNum++;
                    obs = soldier.GetComponent<Soldier>();
                    sub.Attach(obs);
                    soldier.transform.position = soldierPos[i];
                }
            }
    
            void levelUp()
            {
                foreach (GameObject temp in soldierSet)
                {
                    Soldier tempSoldier = temp.GetComponent<Soldier>();
                    tempSoldier.speedUp();
                }
            }
    
            private void Update()
            {
            }
    
            public void getObserver (SceneController obs)
            {
                Subject tempSub = player.GetComponent<Player>();
                tempSub.Attach(obs);
            }
    
            public void getObserver(ScoreManager obs)
            {
                Subject tempSub = player.GetComponent<Player>();
                tempSub.Attach(obs);
            }
    
            public GameObject getPlayer ()
            {
                return player;
            }
        } 
    • ScoreManager
        public class ScoreManager : Observer
        {
            private int lastLoc;
            private int currentLoc;
            private int score;
            private bool playerIsLive;
    
            public int getScore()
            {
                return score;
            }
    
            public void addScore(int s)
            {
                if (playerIsLive)
                {
                    score += s;
                }
            }
    
            public void resetScore()
            {
                score = 0;
            }
    
            void Awake()
            {
                playerIsLive = true;
                score = -1;
                lastLoc = -1;
                currentLoc = -1;
            }
    
            public override void Reaction(bool aLive, Vector3 playPosition, int playLocation)
            {
                playerIsLive = aLive;
                currentLoc = playLocation;
                if (currentLoc != lastLoc)
                {
                    score++;
                    lastLoc = currentLoc;
                }
            }
        }
    • UIController
        public class UIController : MonoBehaviour
        {
            int score;
            int level;
            bool isOver;
    
            GUIStyle fontstyle = new GUIStyle();
            private void Start()
            {
                isOver = false;
                level = 1;
                fontstyle.fontSize = 50;
                fontstyle.normal.textColor = new Color(255, 255, 255);
                fontstyle.alignment = TextAnchor.MiddleCenter;
            }
            private void OnGUI()
            {
                if (isOver)
                {
                    GUI.Button(new Rect(Screen.width / 2, Screen.height / 2, 100, 50), "Game Over", fontstyle);
                }
                if (score == -1)
                {
                    GUI.Button(new Rect(100, 0, 100, 50), "Score: 0", fontstyle);
    
                }
                else
                {
                    GUI.Button(new Rect(100, 0, 100, 50), "Score: " + score, fontstyle);
                }
                GUI.Button(new Rect(100, 50, 100, 50), "Level: " + level, fontstyle);
            }
            public void gameOver ()
            {
                isOver = true;
            }
            public void setScore(int _score)
            {
                score = _score;
            }
            public void setLevel(int _level)
            {
                level = _level;
            }
        }
    • SceneController
        public class SceneController : Observer
        {
            int level;
            Data myData;
            private static SceneController instance;
            UIController UI;
            private Subject sub;
            public static SceneController getInstance()
            {
                if (instance == null)
                    instance = new SceneController();
                return instance;
            }
            private bool isOver;
            ScoreManager scoreManager;
            public override void Reaction(bool aLive, Vector3 playPosition, int playLocation)
            {
                isOver = !aLive;
            }
    
            bool get = false;
    
            // Use this for initialization
            void Start()
            {
                int level = 1;
                isOver = false;
                scoreManager = gameObject.AddComponent<ScoreManager>() as ScoreManager;
                UI = gameObject.AddComponent<UIController>() as UIController;
                myData = gameObject.AddComponent<Data>() as Data;
            }
    
            // Update is called once per frame
            void Update()
            {
                if (!get)
                {
                    myData.getObserver(this);
                    myData.getObserver(scoreManager);
                    get = !get;
                }
                int score = scoreManager.getScore();
                UI.setScore(score);
                if (score >= level * 5)
                {
                    level++;
                    UI.setLevel(level);
                }
                if (isOver)
                {
                    UI.gameOver();
                }
            }
        }
    展开全文
  • 智能巡逻

    2020-11-18 10:41:36
    智能巡逻兵 1. 游戏设计要求: 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵碰撞到障碍物,则会...

    智能巡逻兵

    1. 游戏设计要求:

    1. 创建一个地图和若干巡逻兵(使用动画);
    2. 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    3. 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    4. 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
    5. 失去玩家目标后,继续巡逻;
    6. 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束

    2. 程序设计要求:

    • 必须使用订阅与发布模式传消息
      • subject:OnLostGoal
      • Publisher: ?
      • Subscriber: ?
    • 工厂模式生产巡逻兵

    友善提示1:生成 3~5个边的凸多边型

    • 随机生成矩形
    • 在矩形每个边上随机找点,可得到 3 - 4 的凸多边型

    3. 消息订阅发布模式:

    **Publisher(发布者)**为事件或消息的拥有者,**Subject(渠道)**为发布媒体,**Handle(接收器2)**为处理消息的方法,**Subscriber(订阅者)**为事件或消息感兴趣的人。

    该模式的特点是发布者和订阅者没有直接的耦合,可以进行多对多通讯。

    C# 对订阅/发布模式做了语言级别的实现,称为事件-代理机制(代理模型),发布者要点在于:

    • delegate 关键字定义了函数类型 AttackAction 的代理类型
    • 然后我们申明了 静态 变量 OnAttackAction ,即模型中的 Subject
    • 当 OnAttackAction 被调用时,会自动通知所有侦听者

    订阅者要点在于:

    • 有 Teleport 这样的函数,它的函数签名与 delegate 的定义一样
    • 订阅就是加赋值
    • 取消订阅就是减赋值

    4. UML图

    5.实现过程

    5.1 预期实现

    本次实验会实现一个简单的巡逻者游戏,在一个场景中包含障碍物和几个巡逻者,巡逻者按照既定的巡逻路线行走,当闯入者靠近巡逻者到一定的范围内,巡逻者将开始追捕闯入者,我们需要通过WSAD四个键来控制任务向四个方向的移动,每避开一次巡逻者将得到一分,与巡逻者发生碰撞之后游戏结束。

    5.2 发布者实现:碰撞事件,为巡逻者添加一个碰撞器,当碰到玩家时,发布一个消息。
    public class PlayerCollide : MonoBehaviour
    {
        void OnCollisionEnter(Collision other)
        {
            //当player与zombie相撞,在预制时要为palyer加一个Tag:Player
            if (other.gameObject.tag == "Player")
            {
                Singleton<GameEventManager>.Instance.Gameover();
            }
        }
    }
    
    5.3 发布者实现: 玩家进入巡逻者侦察范围内,开始追踪;玩家逃离巡逻者侦察范围,停止追踪,玩家得分加一。
    //scripts/control/SSActionManager.cs
        public void SSActionEvent(SSAction source, int intParam = 0, GameObject objectParam = null)
        {
            if (intParam == 0)
            {
                //侦查兵跟随玩家
                FollowAction follow = FollowAction.GetSSAction(objectParam.gameObject.GetComponent<PatrolData>().player);
                this.RunAction(objectParam, follow, this);
            }
            else
            {
                //侦察兵继续巡逻
                PatrolAction move = PatrolAction.GetSSAction(objectParam.gameObject.GetComponent<PatrolData>().startPosition);
                this.RunAction(objectParam, move, this);
                //玩家逃脱侦察者追踪范围
                Singleton<GameEventManager>.Instance.Escape();
            }
        }
    
    5.4 定义可订阅的主题 GameEventManer
    //Asserts/Scripts/Control/Event/GameEventManger.cs
    
    public class GameEventManager : MonoBehaviour{
        //分数变化
        public delegate void ScoreEvent();
        public static event ScoreEvent ScoreChange;
        //游戏结束变化
        public delegate void GameoverEvent();
        public static event GameoverEvent GameoverChange;
        //玩家逃脱
        public void Escape(){
            if (ScoreChange != null){
                ScoreChange();  //玩家逃脱,分数加一
            }
        }
        //玩家被捕
        public void Gameover(){
            if (GameoverChange != null){
                GameoverChange();   //玩家与巡逻者碰撞,游戏结束
            }
        }
    }
    
    
    5.5 在场景管理器 FirstController中实现订阅者,订阅上面实现的主题。
        //订阅者模式
        void OnEnable()
        {
            GameEventManager.ScoreChange += AddScore;
            GameEventManager.GameoverChange += Gameover;
        }
    
        void OnDisable()
        {
            GameEventManager.ScoreChange -= AddScore;
            GameEventManager.GameoverChange -= Gameover;
        }
    
        private void AddScore()
        {
            scoreRecorder.Add();
        }
    
        private void Gameover()
        {
            this.gameState = GameState.OVER;
            player.GetComponent<Animator>().enabled = false;
            for (int i = 0; i < patrols.Count; i++)
            {
                patrols[i].GetComponent<Animator>().enabled = false;
            }
            actionManager.DestroyAll();
        }
    
    5.6 动作管理器:父类SSAction与SSActionManger,主要实现了巡逻者实现与追踪的动作切换。
    //SSAction中定义了动作的初始状态、一个回调函数和一个初始化函数Reset
        public bool enable = true;                      //是否正在进行此动作
        public bool destroy = false;                    //是否需要被销毁
    
        public GameObject gameobject;                   //动作对象
        public Transform transform;                     //动作对象的transform
        public ISSActionCallback callback;              //回调函数
    
    //SSActionManger是动作管理的具体实现,实现了巡逻者巡逻与追踪
    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.destroy)
                {
                    waitingDelete.Add(ac.GetInstanceID());
                }
                else if (ac.enable)
                {
                    //运动学运动更新
                    ac.Update();
                }
            }
    
            foreach (int key in waitingDelete)
            {
                SSAction ac = actions[key];
                actions.Remove(key);
                Destroy(ac);
            }
            waitingDelete.Clear();
        }
    //删除动作
        public void DestroyAll()
        {
            foreach (KeyValuePair<int, SSAction> kv in actions)
            {
                SSAction ac = kv.Value;
                ac.destroy = true;
            }
        }
    
    5.7 场景控制器调用动作管理器的接口 :PartActionManager
    public class PatrolActionManager : SSActionManager
    {
        //外界调用的动作
        private PatrolAction patrolAction;                            //巡逻兵巡逻
        //外界调用动作的接口
        public void Patrol(GameObject patrol)
        {
            patrolAction = PatrolAction.GetSSAction(patrol.transform.position);
            this.RunAction(patrol, patrolAction, this);
        }
        //判断游戏结束,停止所有动作
        public void DestroyAllAction()
        {
            DestroyAll();
        }
    }
    
    5.8 巡逻者的巡逻动作实现:PatrolAction
       //巡逻兵参数
        private enum Dirction { EAST, NORTH, WEST, SOUTH };
        private float posX, posZ;                 //初始x和z方向坐标
        private float moveLength;                  //移动的长度
        private float moveSpeed = 1.2f;            //移动速度
        private bool moveSign = true;              //是否到达目的地
        private Dirction dirction = Dirction.WEST;  //移动的方向
        private PatrolData data;                    //侦察兵的数据
    
     //定义巡逻动作,其它实现细节请看具体文件  
     private void Patrol()
        {
            if (moveSign)
            {
                //不需要转向则设定一个目的地,按照矩形移动
                switch (dirction)
                {
                    case Dirction.EAST:
                        posX -= moveLength;
                        break;
                    case Dirction.NORTH:
                        posZ += moveLength;
                        break;
                    case Dirction.WEST:
                        posX += moveLength;
                        break;
                    case Dirction.SOUTH:
                        posZ -= moveLength;
                        break;
                }
                moveSign = false;
            }
            this.transform.LookAt(new Vector3(posX, 0, posZ));
            float distance = Vector3.Distance(transform.position, new Vector3(posX, 0, posZ));
    
            //当前位置与目的地距离浮点数的比较决定是否转向
            if (distance > 0.9)
            {
                transform.position = Vector3.MoveTowards(this.transform.position, new Vector3(posX, 0, posZ), moveSpeed * Time.deltaTime);
            }
            else
            {
                dirction = dirction + 1;
                if (dirction > Dirction.SOUTH)
                {
                    dirction = Dirction.EAST;
                }
                moveSign = true;
            }
        }
    
    5.9 巡逻者追踪动作:FollowAction
        //侦察时的动作
        private float speed = 2f;            //跟随玩家的速度
        private GameObject player;           //玩家
        private PatrolData data;             //侦查兵数据
    
        //如果侦察兵没有跟随对象,或者需要跟随的玩家不在侦查兵的区域内,销毁追踪动作,调用回调函数。
        if (data.wallSign != data.sign)
         {
             this.destroy = true;
             this.enable = false;
             this.callback.SSActionEvent(this, 1, this.gameobject);
         }
    
    //追踪实现
    void Follow(){
       this.transform.position = Vector3.MoveTowards(this.transform.position, player.transform.position, speed * Time.deltaTime);
       this.transform.LookAt(player.transform.position);
    }
    
    5.10 巡逻者工厂模式:PatrolFactory实现巡逻兵的生产
    //实现对象池:
    
    private GameObject patrol = null;
    //巡逻兵
    private List<GameObject> used = new List<GameObject>();
    //正在被使用的巡逻兵,该游戏巡逻兵不需要回收,所以不需要free表
    private Vector3[] position = new Vector3[9];
    //保存每个巡逻兵的初始位置
    public FirstController sceneControler; 
    //场景控制器
    
    //巡逻兵设置
        public List<GameObject> GetPatrols()
        {
            int[] pos_x = { 1, -4};
            int[] pos_z = { 4, -1};
            int index = 0;
            //生成不同的巡逻兵初始位置
            for (int i = 0; i < 2; i++)
            {
                for (int j = 0; j < 2; j++)
                {
                    position[index] = new Vector3(pos_x[i], 0, pos_z[j]);
                    index++;
                }
            }
            for (int i = 0; i < 4; i++)
            {
                patrol = Instantiate(Resources.Load<GameObject>("Prefabs/zimbie"));
                patrol.transform.position = position[i];
                patrol.AddComponent<PatrolData>();
                patrol.GetComponent<PatrolData>().sign = i + 1;
                patrol.GetComponent<PatrolData>().startPosition = position[i];
                used.Add(patrol);
            }
            return used;
        }
    
    5.11 UserGUI实现图形交互界面和人机互动操作(WSAD控制玩家)
       //实现WSAD移动
       void Update()
        {
            //获取方向键的偏移量
            float translationX = Input.GetAxis("Horizontal");
            float translationZ = Input.GetAxis("Vertical");
            //移动玩家
            if (action.GetGameState() == GameState.RUNNING)
                action.MovePlayer(translationX, translationZ);
        }
    
    5.12 其它Singleton、ISceneController、ScoreRecoder、SSdirector、ISActionCallback的实现和上一次作业没有区别,可以直接使用。

    6. 预制材料

    1. 设计一个Plane让玩家和巡逻者在上面运动:
      在这里插入图片描述

    2. 设计两个Animator分别为player与zimbie
      在这里插入图片描述

    3. 为player添加对应的animator、Avatar、Box Collider、Rigidbody

    7. 效果展示:Patrol

    8. 不足与改进:

    场景中四个巡逻兵的动作相同,较为简单,player有时会跑出围栏外边。
    可以考虑为player添加翻滚、跳跃的动作。

    展开全文
  • 巡逻兵游戏

    2020-11-18 23:33:03
    1、 巡逻兵 提交要求: 游戏设计要求: 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵碰撞到障碍物...

    1、 巡逻兵

    提交要求:

    游戏设计要求:

    创建一个地图和若干巡逻兵(使用动画);
    每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    巡逻兵在设定范围内感知到玩家,会自动追击玩家;
    失去玩家目标后,继续巡逻;
    计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
    程序设计要求:
    必须使用订阅与发布模式传消息
    subject:OnLostGoal
    Publisher: ?
    Subscriber: ?
    工厂模式生产巡逻兵
    友善提示1:生成 3~5个边的凸多边型
    随机生成矩形
    在矩形每个边上随机找点,可得到 3 - 4 的凸多边型
    5 ?
    友善提示2:参考以前博客,给出自己新玩法
    视频
    源码

    GameEventManager

    游戏事件管理器,管理游戏中信息的传递。订阅发布者模式就是依赖这个类实现的。
    这个是subject发布者,作为事件通道。例如处理巡逻兵运动的类,就是一个subscriber接受者。而如处理水晶碰撞的类和处理人物碰撞的类就是一个publisher,发布信息。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class GameEventManager : MonoBehaviour
    {
        public delegate void ScoreEvent();
        public static event ScoreEvent ScoreChange;
        public delegate void GameoverEvent();
        public static event GameoverEvent GameoverChange;
        public delegate void CrystalEvent();
        public static event CrystalEvent CrystalChange;
    
        public void PlayerEscape()
        {
            if (ScoreChange != null)
            {
                ScoreChange();
            }
        }
        public void PlayerGameover()
        {
            if (GameoverChange != null)
            {
                GameoverChange();
            }
        }
        public void ReduceCrystalNum()
        {
            if (CrystalChange != null)
            {
                CrystalChange();
            }
        }
    }
    
    

    AreaCollide

    用来处理地图冲突。将地图分成九个区域,让怪物最多在自己原来的区域活动,防止怪物追出来导致游戏难度过大。每一个区域有每一个区域有自己的标志,这个用来处理玩家进入不同区域时给玩家打上的标签。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class AreaCollide : MonoBehaviour
    {
        public int sign = 0;
        FirstSceneController sceneController;
        private void Start()
        {
            sceneController = SSDirector.GetInstance().CurrentScenceController as FirstSceneController;
        }
        void OnTriggerEnter(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                sceneController.wall_sign = sign;
            }
        }
    }
    
    

    CameraFlow

    用来设定摄像机跟随玩家。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CameraFlow : MonoBehaviour
    {
        public GameObject follow;            
        public float speed = 10f;          
        Vector3 offset;                     
    
        void Start()
        {
            offset = transform.position - follow.transform.position;
        }
    
        void FixedUpdate()
        {
            Vector3 target = follow.transform.position + offset;
            transform.position = Vector3.Lerp(transform.position, target, speed * Time.deltaTime);
        }
    }
    

    CrystalCollide

    用来处理水晶和操纵的角色碰撞,用来判定得分。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CrystalCollide : MonoBehaviour
    {
        void OnTriggerEnter(Collider collider)
        {
            if (collider.gameObject.tag == "Player" && this.gameObject.activeSelf)
            {
                this.gameObject.SetActive(false);
                Singleton<GameEventManager>.Instance.ReduceCrystalNum();
            }
        }
    }
    
    

    GoPatrolAction

    巡逻兵的运动函数,用来管理巡逻兵在没有判定到人时的自运动

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class GoPatrolAction : SSAction
    {
        private enum Dirction { EAST, NORTH, WEST, SOUTH };
        private float posX, posZ;                 
        private float move_length;                 
        private float move_speed = 1f;            
        private bool move_sign = true;              
        private Dirction dirction = Dirction.EAST;  
        private PatrolData data;                    
        
    
        private GoPatrolAction() { }
        public static GoPatrolAction GetSSAction(Vector3 location)
        {
            GoPatrolAction action = CreateInstance<GoPatrolAction>();
            action.posX = location.x;
            action.posZ = location.z;
            action.move_length = Random.Range(4, 7);
            return action;
        }
        public override void Update()
        {
            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);
            }
            Gopatrol();
            if (data.follow_player && data.wall_sign == data.sign)
            {
                this.destroy = true;
                this.callback.SSActionEvent(this,0,this.gameobject);
            }
        }
        public override void Start()
        {
            this.gameobject.GetComponent<Animator>().SetBool("run", true);
            data  = this.gameobject.GetComponent<PatrolData>();
        }
    
        void Gopatrol()
        {
            if (move_sign)
            {
                switch (dirction)
                {
                    case Dirction.EAST:
                        posX -= move_length;
                        break;
                    case Dirction.NORTH:
                        posZ += move_length;
                        break;
                    case Dirction.WEST:
                        posX += move_length;
                        break;
                    case Dirction.SOUTH:
                        posZ -= move_length;
                        break;
                }
                move_sign = false;
            }
            this.transform.LookAt(new Vector3(posX, 0, posZ));
            float distance = Vector3.Distance(transform.position, new Vector3(posX, 0, posZ));
            if (distance > 0.9)
            {
                transform.position = Vector3.MoveTowards(this.transform.position, new Vector3(posX, 0, posZ), move_speed * Time.deltaTime);
            }
            else
            {
                dirction = dirction + 1;
                if(dirction > Dirction.SOUTH)
                {
                    dirction = Dirction.EAST;
                }
                move_sign = true;
            }
        }
    }
    

    PatrolCollide

    判定操控角色是否进入巡逻兵的追击范围。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PatrolCollide : MonoBehaviour
    {
        void OnTriggerEnter(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                this.gameObject.transform.parent.GetComponent<PatrolData>().follow_player = true;
                this.gameObject.transform.parent.GetComponent<PatrolData>().player = collider.gameObject;
            }
        }
        void OnTriggerExit(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                this.gameObject.transform.parent.GetComponent<PatrolData>().follow_player = false;
                this.gameObject.transform.parent.GetComponent<PatrolData>().player = null;
            }
        }
    }
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PatrolCollide : MonoBehaviour
    {
        void OnTriggerEnter(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                this.gameObject.transform.parent.GetComponent<PatrolData>().follow_player = true;
                this.gameObject.transform.parent.GetComponent<PatrolData>().player = collider.gameObject;
            }
        }
        void OnTriggerExit(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                this.gameObject.transform.parent.GetComponent<PatrolData>().follow_player = false;
                this.gameObject.transform.parent.GetComponent<PatrolData>().player = null;
            }
        }
    }
    

    PatrolFollowAction

    操纵角色进入巡逻兵的追击范围时巡逻兵的追击动作

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PatrolFollowAction : SSAction
    {
        private float speed = 1.5f;            
        private GameObject player;           
        private PatrolData data;             
    
        private PatrolFollowAction() { }
        public static PatrolFollowAction GetSSAction(GameObject player)
        {
            PatrolFollowAction action = CreateInstance<PatrolFollowAction>();
            action.player = player;
            return action;
        }
    
        public override void Update()
        {
            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);
            }
             
            Follow();
            if (!data.follow_player || data.wall_sign != data.sign)
            {
                this.destroy = true;
                this.callback.SSActionEvent(this,1,this.gameobject);
            }
        }
        public override void Start()
        {
            data = this.gameobject.GetComponent<PatrolData>();
        }
        void Follow()
        {
            transform.position = Vector3.MoveTowards(this.transform.position, player.transform.position, speed * Time.deltaTime);
            this.transform.LookAt(player.transform.position);
        }
    }
    

    PlayerCollide

    操纵角色被巡逻兵追上的时候判定死亡的类。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PlayerCollide : MonoBehaviour
    {
    
        void OnCollisionEnter(Collision other)
        {
            if (other.gameObject.tag == "Player")
            {
                other.gameObject.GetComponent<Animator>().SetTrigger("death");
                this.GetComponent<Animator>().SetTrigger("shoot");
                Singleton<GameEventManager>.Instance.PlayerGameover();
            }
        }
    }
    

    PropFactory

    对象工厂,用来创造新的巡逻兵,水晶,和管理已经出现的巡逻兵,水晶等对象

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PropFactory : MonoBehaviour
    {
        private GameObject patrol = null;                             
        private List<GameObject> used = new List<GameObject>();        
        private GameObject crystal = null;                             
        private List<GameObject> usedcrystal = new List<GameObject>();      
        private float range = 12;                                      
        private Vector3[] vec = new Vector3[9];                        
    
        public FirstSceneController sceneControler;                    
    
        public List<GameObject> GetPatrols()
        {
            int[] pos_x = { -6, 4, 13 };
            int[] pos_z = { -4, 6, -13 };
            int index = 0;
            for(int i=0;i < 3;i++)
            {
                for(int j=0;j < 3;j++)
                {
                    vec[index] = new Vector3(pos_x[i], 0, pos_z[j]);
                    index++;
                }
            }
            for(int i=0; i < 9; i++)
            {
                patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));
                patrol.transform.position = vec[i];
                patrol.GetComponent<PatrolData>().sign = i + 1;
                patrol.GetComponent<PatrolData>().start_position = vec[i];
                used.Add(patrol);
            }   
            return used;
        }
    
    
        public List<GameObject> GetCrystal()
        {
            for(int i=0;i<12;i++)
            {
                crystal = Instantiate(Resources.Load<GameObject>("Prefabs/Crystal"));
                float ranx = Random.Range(-range, range);
                float ranz = Random.Range(-range, range);
                crystal.transform.position = new Vector3(ranx, 0, ranz);
                usedcrystal.Add(crystal);
            }
    
            return usedcrystal;
        }
        public void StopPatrol()
        {
            for (int i = 0; i < used.Count; i++)
            {
                used[i].gameObject.GetComponent<Animator>().SetBool("run", false);
            }
        }
    }
    
    

    ScoreRecorder

    得分记录器,在被巡逻兵追击时逃脱得一分,吃到水晶得一分。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ScoreRecorder : MonoBehaviour
    {
        public FirstSceneController sceneController;
        public int score = 0;                           
        public int crystal_number = 5;                 
    
        void Start()
        {
            sceneController = (FirstSceneController)SSDirector.GetInstance().CurrentScenceController;
            sceneController.recorder = this;
        }
        public int GetScore()
        {
            return score;
        }
        public void AddScore()
        {
            score++;
        }
        public int GetCrystalNumber()
        {
            return crystal_number;
        }
        public void ReduceCrystal()
        {
            crystal_number--;
        }
    }
    
    
    展开全文
  • BFS 巡逻机器人

    2015-07-26 19:49:00
    机器人在一个矩形区域巡逻,是一网格(m行和n列)。从左上角(1,1)到右下角(m,n)。网络格 中的一些格子是空地(用0表示),其他格子是障碍(用1表示)。机器人每次有4个方向走,但不能 连续穿越k障碍,求...
  • Unity3D-智能巡逻

    2018-05-09 18:30:36
    智能巡逻兵 游戏基本内容 ...巡逻兵无法跨越区域,当碰到每个区域的出入口时被消灭,在所属区域内重新生成新的的巡逻兵,进行巡逻 以一个随机的四边形路线巡逻 碰到墙壁后重新生成巡逻路线 当玩...
  • unity 智能巡逻

    2020-11-18 20:34:34
    unity 智能巡逻兵 一、游戏要求 游戏设计要求: 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵...
  • Unity3D 巡逻

    2018-05-11 23:27:16
    智能巡逻兵 参考博客: Unity3d学习之路-简单巡逻兵 (偷了一下懒,主人公和巡逻兵的预制体均为上述博客博主所用的预制体) 游戏和设计要求: 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸...
  • unity3d开发巡逻兵游戏

    2018-05-11 16:59:53
    * 创建一个地图和若干巡逻兵(使用动画); * 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; * 巡逻兵碰撞到障碍物,则会自动选下一个点为目标; * ...
  • unity实现简单巡逻

    2020-11-18 21:56:16
    unity实现简单巡逻兵 游戏要求 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵碰撞到障碍物,则会...
  • Unity3d-简单巡逻

    千次阅读 2018-05-10 09:49:59
    每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵碰撞到障碍物,则会自动选下一个点为目标; 巡逻兵在设定范围内感知到玩家,会自动追击玩家...
  • 七、智能巡逻

    2020-11-11 09:52:02
    1、智能巡逻兵 提交要求: 游戏设计要求: 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵碰撞到...
  • Unity之智能巡逻

    2019-11-03 23:41:18
    智能巡逻兵 游戏设计要求: 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵碰撞到障碍物,则会自动...
  • 我们可以根据三个采油区域在横向和纵向上的重叠情况分成六种分布: (1)可以用两条横线分割 (2)可以用两条纵线分割 (3)可以分成左右两部分,其中左边可以分上下两部分 (4)……………………………,其中...
  • unity3d自动巡逻

    2019-10-26 22:52:15
    创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 巡逻兵碰撞到障碍物,则会自动选下一个点为目标; 巡逻兵在...
  • 作业内容:游戏设计要求:创建一个地图和若干巡逻兵(使用动画);每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;巡逻兵碰撞到障碍物,则会自动选下...
  • Unity 3D 巡逻兵设计

    2018-05-11 23:46:37
    游戏设计要求: 创建一个地图和若干巡逻兵(使用动画); 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用...计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束; ...
  • 自动巡逻

    2018-05-10 15:22:51
    turnNextDirection() 函数用使物体当碰到墙壁或者出了巡逻区域后,往反方向走。 public void turnNextDirection() { // 销毁当前动作 currentAction.destory = true ; // 往相反方向走 switch ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,667
精华内容 666
关键字:

区域巡逻可以