当前位置:首页 > 技术 > 正文内容

在 Nez 中实现碰撞检测

访客 技术 2026年7月25日 1

在 Nez 中实现碰撞检测

在游戏开发中,碰撞检测是实现玩家互动和环境交互的重要功能。本文将介绍如何在 Nez 框架中使用碰撞检测功能,包括几种常见的碰撞器实现及其扩展。

一、Nez 中的碰撞器实现

Nez 提供了三种内置碰撞器:BoxCollider(矩形碰撞器)、CircleCollider(圆形碰撞器)和 PolygonCollider(多边形碰撞器)。这些碰撞器能够检测不同形状的碰撞区域。

为了实现碰撞进入和离开事件,我们对这些碰撞器进行了扩展:

    internal class CollisionStateManager
    {
        #region 私有字段
        private Dictionary<Collider, bool> _collisionMap;
        private List<Collider> _removeList;
        #endregion

        #region 事件
        public event EventHandler<Collider> OnCollisionEnter;
        public event EventHandler<Collider> OnCollisionExit;
        #endregion

        #region 构造函数
        public CollisionStateManager()
        {
            _collisionMap = new Dictionary<Collider, bool>();
            _removeList = new List<Collider>();
        }
        #endregion

        #region 方法
        public void ResetState()
        {
            var keys = _collisionMap.Keys.ToImmutableArray();

            foreach (var collider in keys)
            {
                _collisionMap[collider] = false;
            }
        }

        public void Add(Collider obj)
        {
            if (_collisionMap.ContainsKey(obj))
            {
                _collisionMap[obj] = true;
            }
            else
            {
                _collisionMap.Add(obj, true);
                OnCollisionEnter?.Invoke(this, obj);
            }
        }

        public void Cleanup()
        {
            if (_collisionMap.Count == 0) return;
            _removeList.Clear();
            foreach (var key in _collisionMap.Keys)
            {
                if (_collisionMap[key] == false)
                    _removeList.Add(key);
            }

            foreach (var key in _removeList)
            {
                _collisionMap.Remove(key);
                OnCollisionExit?.Invoke(this, key);
            }
        }
        #endregion
    }

    public interface ICollisionNotifier
    {
        event EventHandler<Collider> OnCollisionEnter;
        event EventHandler<Collider> OnCollisionExit;
    }

    public class BoxCollisionHandler : BoxCollider, IUpdatable, ICollisionNotifier
    {
        #region 私有字段
        private List<CollisionResult> _results = new List<CollisionResult>();
        private CollisionStateManager _stateManager = new CollisionStateManager();
        #endregion

        #region 事件
        public event EventHandler<Collider> OnCollisionEnter;
        public event EventHandler<Collider> OnCollisionExit;
        #endregion

        #region 构造函数
        public BoxCollisionHandler()
        {
            _stateManager.OnCollisionEnter += HandleCollisionEnter;
            _stateManager.OnCollisionExit += HandleCollisionExit;
        }
        #endregion

        #region 事件处理
        private void HandleCollisionExit(object? sender, Collider e)
        {
            if (e is Collider collider)
                OnCollisionExit?.Invoke(this, collider);
        }

        private void HandleCollisionEnter(object? sender, Collider e)
        {
            if (e is Collider collider)
                OnCollisionEnter?.Invoke(this, collider);
        }
        #endregion

        #region 更新逻辑
        public void Update()
        {
            if (!Enabled) return;
            _stateManager.ResetState();

            if (CollidesWithAll(ref _results))
            {
                foreach (var result in _results)
                {
                    _stateManager.Add(result.Collider);
                }
            }

            _stateManager.Cleanup();
        }
        #endregion
    }

    public class CircleCollisionHandler : CircleCollider, IUpdatable, ICollisionNotifier
    {
        #region 私有字段
        private List<CollisionResult> _results = new List<CollisionResult>();
        private CollisionStateManager _stateManager = new CollisionStateManager();
        #endregion

        #region 事件
        public event EventHandler<Collider> OnCollisionEnter;
        public event EventHandler<Collider> OnCollisionExit;
        #endregion

        #region 构造函数
        public CircleCollisionHandler()
        {
            _stateManager.OnCollisionEnter += HandleCollisionEnter;
            _stateManager.OnCollisionExit += HandleCollisionExit;
        }
        #endregion

        #region 事件处理
        private void HandleCollisionExit(object? sender, Collider e)
        {
            if (e is Collider collider)
                OnCollisionExit?.Invoke(this, collider);
        }

        private void HandleCollisionEnter(object? sender, Collider e)
        {
            if (e is Collider collider)
                OnCollisionEnter?.Invoke(this, collider);
        }
        #endregion

        #region 更新逻辑
        public void Update()
        {
            if (!Enabled) return;
            _stateManager.ResetState();

            if (CollidesWithAll(ref _results))
            {
                foreach (var result in _results)
                {
                    _stateManager.Add(result.Collider);
                }
            }

            _stateManager.Cleanup();
        }
        #endregion
    }

    public class PolygonCollisionHandler : PolygonCollider, IUpdatable, ICollisionNotifier
    {
        #region 私有字段
        private List<CollisionResult> _results = new List<CollisionResult>();
        private CollisionStateManager _stateManager = new CollisionStateManager();
        #endregion

        #region 事件
        public event EventHandler<Collider> OnCollisionEnter;
        public event EventHandler<Collider> OnCollisionExit;
        #endregion

        #region 构造函数
        public PolygonCollisionHandler()
        {
            _stateManager.OnCollisionEnter += HandleCollisionEnter;
            _stateManager.OnCollisionExit += HandleCollisionExit;
        }
        #endregion

        #region 事件处理
        private void HandleCollisionExit(object? sender, Collider e)
        {
            if (e is Collider collider)
                OnCollisionExit?.Invoke(this, collider);
        }

        private void HandleCollisionEnter(object? sender, Collider e)
        {
            if (e is Collider collider)
                OnCollisionEnter?.Invoke(this, collider);
        }
        #endregion

        #region 更新逻辑
        public void Update()
        {
            if (!Enabled) return;
            _stateManager.ResetState();

            if (CollidesWithAll(ref _results))
            {
                foreach (var result in _results)
                {
                    _stateManager.Add(result.Collider);
                }
            }

            _stateManager.Cleanup();
        }
        #endregion
    }

二、如何使用碰撞检测

使用 Nez 的碰撞检测功能需要以下条件:

  1. 项目中需要包含 XNA 框架(如 FNA 或 MonoGame)的相关依赖项。
  2. 确保项目中引用了 Nez 的正确版本。

下面是一个简单的示例代码,展示了如何创建一个跟随鼠标移动的圆形物体和一个矩形碰撞区域:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Nez;

namespace CollisionTest
{
    public class GameScene : Scene
    {

        private Entity CreateSphere()
        {
            var sphere = CreateEntity("sphere");
            var collider = new CircleCollisionHandler();
            collider.Radius = 30;
            sphere.AddComponent(collider);
            return sphere;
        }

        private Entity CreateRectArea()
        {
            var rectArea = CreateEntity("rect_area");
            var collider = new BoxCollisionHandler();
            collider.SetSize(400, 400);
            rectArea.AddComponent(collider);
            collider.OnCollisionEnter += Collider_OnCollisionEnter;
            collider.OnCollisionExit += Collider_OnCollisionExit;
            rectArea.SetPosition(new Vector2(250, 250));
            return rectArea;
        }

        private void Collider_OnCollisionEnter(object? sender, Collider e)
        {
            Console.WriteLine($"{e.Entity.Name} entered collision area!");
        }
        private void Collider_OnCollisionExit(object? sender, Collider e)
        {
            Console.WriteLine($"{e.Entity.Name} exited collision area!");
        }
        public override void Initialize()
        {
            base.Initialize();
            ClearColor = Color.Black;
            Core.DebugRenderEnabled = true;

            CreateSphere();
            CreateRectArea();
        }

        public override void Update()
        {
            base.Update();
            var mouseState = Mouse.GetState();
            var sphere = Entities.FindEntity("sphere");
            if (sphere is not null)
            {
                sphere.Position = new Vector2(mouseState.X, mouseState.Y);
            }
        }
    }

    public class GameCore : Core
    {
        protected override void Initialize()
        {
            base.Initialize();
            Scene = new GameScene();
        }

        static void Main(string[] args)
        {
            using (var game = new GameCore())
            {
                game.Run();
            }
        }
    }
}

三、碰撞检测的应用

通过上述代码,你可以看到当小球进入矩形区域时,控制台会输出相应的日志信息。这种机制可以应用于许多场景,比如触发攻击范围、检测视野区域或实现 UI 热区响应。

总结来说,Nez 提供了强大的碰撞检测功能,能够帮助开发者更高效地实现游戏中的各种交互逻辑。

标签: Nez

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。