OpenRA开源红色警戒游戏RPG源码WAngle.cs解读

python编程示例系列
python编程示例系列二
python的Web神器Streamlit
如何应聘高薪职位
C#视觉应用开发问题系列
c#串口应用开发问题系列
microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
OpenRA开源红色警戒游戏RPG源码解读
在这里插入图片描述# WAngle 结构详解与分析

代码注释

using System;
using Eluant;                  // 引入Eluant库,用于Lua脚本集成
using Eluant.ObjectBinding;    // 引入Eluant对象绑定功能
using OpenRA.Scripting;        // 引入OpenRA脚本功能

namespace OpenRA
{
    /// <summary>
    /// 1D角度 - 1024单位 = 360度。
    /// 这是OpenRA游戏引擎中用于表示角度的结构
    /// </summary>
    public readonly struct WAngle : IEquatable<WAngle>, IScriptBindable,
        ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, ILuaToStringBinding
    {
        // 角度值,范围为0-1023
        public readonly int Angle;
        // 角度的平方值,用于某些计算
        public int AngleSquared => Angle * Angle;

        /// <summary>
        /// 构造函数,确保角度值在0-1023范围内
        /// </summary>
        /// <param name="a">输入的角度值</param>
        public WAngle(int a)
        {
            // 对1024取模,确保在0-1023范围内
            Angle = a % 1024;
            // 如果为负数,加上1024使其变为正数
            if (Angle < 0)
                Angle += 1024;
        }

        // 零角度常量
        public static readonly WAngle Zero = new(0);
        
        // 从朝向值创建角度,朝向值乘以4转换为角度
        public static WAngle FromFacing(int facing) { return new WAngle(facing * 4); }
        
        // 从度数创建角度,度数乘以1024/360转换为内部角度表示
        public static WAngle FromDegrees(int degrees) { return new WAngle(degrees * 1024 / 360); }
        
        // 重载加法运算符
        public static WAngle operator +(WAngle a, WAngle b) { return new WAngle(a.Angle + b.Angle); }
        
        // 重载减法运算符
        public static WAngle operator -(WAngle a, WAngle b) { return new WAngle(a.Angle - b.Angle); }
        
        // 重载负号运算符
        public static WAngle operator -(WAngle a) { return new WAngle(-a.Angle); }

        // 重载相等运算符
        public static bool operator ==(WAngle me, WAngle other) { return me.Angle == other.Angle; }
        
        // 重载不等运算符
        public static bool operator !=(WAngle me, WAngle other) { return me.Angle != other.Angle; }

        // 重写GetHashCode方法
        public override int GetHashCode() { return Angle.GetHashCode(); }

        // 实现IEquatable接口的Equals方法
        public bool Equals(WAngle other) { return other == this; }
        
        // 重写Object.Equals方法
        public override bool Equals(object obj) { return obj is WAngle angle && Equals(angle); }

        // 获取朝向值,角度除以4
        public int Facing => Angle / 4;

        /// <summary>
        /// 计算正弦值,利用余弦表和角度偏移实现
        /// </summary>
        public int Sin() { return new WAngle(Angle - 256).Cos(); }

        /// <summary>
        /// 计算余弦值,使用预计算的余弦表
        /// </summary>
        public int Cos()
        {
            // 角度在0-256范围内,直接使用余弦表
            if (Angle <= 256)
                return CosineTable[Angle];
            // 角度在257-512范围内,使用对称性计算
            if (Angle <= 512)
                return -CosineTable[512 - Angle];
            // 角度大于512,递归计算
            return -new WAngle(Angle - 512).Cos();
        }

        /// <summary>
        /// 计算正切值,使用预计算的正切表
        /// </summary>
        public int Tan()
        {
            // 角度在0-256范围内,直接使用正切表
            if (Angle <= 256)
                return TanTable[Angle];
            // 角度在257-512范围内,使用对称性计算
            if (Angle <= 512)
                return -TanTable[512 - Angle];
            // 角度大于512,递归计算
            return new WAngle(Angle - 512).Tan();
        }

        /// <summary>
        /// 线性插值两个角度,考虑角度环绕问题
        /// </summary>
        /// <param name="a">起始角度</param>
        /// <param name="b">结束角度</param>
        /// <param name="mul">插值分子</param>
        /// <param name="div">插值分母</param>
        public static WAngle Lerp(WAngle a, WAngle b, int mul, int div)
        {
            // 将1024<->0的环绕映射到线性空间
            var aa = a.Angle;
            var bb = b.Angle;
            
            // 如果两个角度相差超过半圈,调整值使其取最短路径
            if (aa > bb && aa - bb > 512)
                aa -= 1024;

            if (bb > aa && bb - aa > 512)
                bb -= 1024;

            // 计算插值结果
            return new WAngle(aa + (bb - aa) * mul / div);
        }

        /// <summary>
        /// 计算反正弦,根据值查找最接近的角度
        /// </summary>
        /// <param name="d">正弦值,范围-1024到1024</param>
        public static WAngle ArcSin(int d)
        {
            // 检查输入值是否在有效范围内
            if (d < -1024 || d > 1024)
                throw new ArgumentException($"ArcSin is only valid for values between -1024 and 1024. Received {d}");

            // 使用余弦表查找最接近的索引
            var a = ClosestCosineIndex(Math.Abs(d));
            // 根据正负值计算角度
            return new WAngle(d < 0 ? 768 + a : 256 - a);
        }

        /// <summary>
        /// 计算反余弦,根据值查找最接近的角度
        /// </summary>
        /// <param name="d">余弦值,范围-1024到1024</param>
        public static WAngle ArcCos(int d)
        {
            // 检查输入值是否在有效范围内
            if (d < -1024 || d > 1024)
                throw new ArgumentException($"ArcCos is only valid for values between -1024 and 1024. Received {d}");

            // 使用余弦表查找最接近的索引
            var a = ClosestCosineIndex(Math.Abs(d));
            // 根据正负值计算角度
            return new WAngle(d < 0 ? 512 - a : a);
        }

        /// <summary>
        /// 查找余弦表中最接近给定值的索引
        /// 使用二分查找算法提高效率
        /// </summary>
        /// <param name="value">要查找的值</param>
        static int ClosestCosineIndex(int value)
        {
            // 二分查找的上下边界
            var aboveIndex = 0;
            var belowIndex = 256;
            
            // 二分查找循环
            while (aboveIndex != belowIndex - 1)
            {
                var index = (aboveIndex + belowIndex) / 2;
                var val = CosineTable[index];

                // 找到精确匹配
                if (val == value)
                    return index;

                // 调整搜索范围
                if (val < value)
                    belowIndex = index;
                else
                    aboveIndex = index;
            }

            // 取误差最小的索引
            return CosineTable[aboveIndex] - value > value - CosineTable[belowIndex] ? belowIndex : aboveIndex;
        }

        /// <summary>
        /// 计算反正切,重载版本,使用默认步长1
        /// </summary>
        public static WAngle ArcTan(int y, int x) { return ArcTan(y, x, 1); }
        
        /// <summary>
        /// 计算反正切,根据x和y坐标找到角度
        /// </summary>
        /// <param name="y">y坐标</param>
        /// <param name="x">x坐标</param>
        /// <param name="stride">步长,用于优化搜索</param>
        public static WAngle ArcTan(int y, int x, int stride)
        {
            // 特殊情况:y为0
            if (y == 0)
                return new WAngle(x >= 0 ? 0 : 512); // 0度或180度

            // 特殊情况:x为0
            if (x == 0)
                return new WAngle(Math.Sign(y) * 256); // 90度或270度

            // 取绝对值简化计算
            var ay = Math.Abs(y);
            var ax = Math.Abs(x);

            // 找到最接近的满足y = x*tan(theta)的角度
            // 使用long类型存储bestVal避免整数溢出
            var bestVal = long.MaxValue;
            var bestAngle = 0;
            
            // 在正切表中搜索最佳匹配
            for (var i = 0; i < 256; i += stride)
            {
                var val = Math.Abs(1024 * ay - (long)ax * TanTable[i]);
                if (val < bestVal)
                {
                    bestVal = val;
                    bestAngle = i;
                }
            }

            // 根据x和y的符号计算实际象限
            if (x < 0 && y > 0)
                bestAngle = 512 - bestAngle;      // 第二象限
            else if (x < 0 && y < 0)
                bestAngle = 512 + bestAngle;      // 第三象限
            else if (x > 0 && y < 0)
                bestAngle = 1024 - bestAngle;     // 第四象限
            // 第一象限不需要调整

            return new WAngle(bestAngle);
        }

        /// <summary>
        /// 转换为弧度制,仅用于渲染代码
        /// </summary>
        public float RendererRadians() { return (float)(Angle * Math.PI / 512f); }
        
        /// <summary>
        /// 转换为角度制,仅用于渲染代码
        /// </summary>
        public float RendererDegrees() { return Angle * 0.3515625f; } // 360/1024

        /// <summary>
        /// 重写ToString方法,返回角度值的字符串表示
        /// </summary>
        public override string ToString() { return Angle.ToStringInvariant(); }

        /// <summary>
        /// 预计算的余弦表,范围0-256对应0-90度
        /// 值范围为0-1024,相当于将-1到1的余弦值映射到-1024到1024
        /// </summary>
        static readonly int[] CosineTable =
        {
            // 详细的余弦值表...
            1024, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1022, 1022, 1022, 1021,
            // 中间省略...
            56, 50, 43, 37, 31, 25, 18, 12, 6, 0
        };

        /// <summary>
        /// 预计算的正切表,范围0-256对应0-90度
        /// </summary>
        static readonly int[] TanTable =
        {
            // 详细的正切值表...
            0, 6, 12, 18, 25, 31, 37, 44, 50, 56, 62, 69, 75, 81, 88, 94, 100, 107,
            // 中间省略...
            23826, 27801, 33366, 41713, 55622, 83438, 166883, int.MaxValue
        };

        #region Scripting interface

        /// <summary>
        /// 实现Lua加法操作的接口方法
        /// </summary>
        public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
        {
            // 尝试获取左操作数
            if (!left.TryGetClrValue(out WAngle a))
                throw new LuaException(
                    "Attempted to call WAngle.Add(WAngle, WAngle) with invalid arguments " +
                    $"({left.WrappedClrType().Name}, {right.WrappedClrType().Name})");

            // 尝试获取右操作数并执行加法
            if (right.TryGetClrValue(out WAngle b))
                return new LuaCustomClrObject(a + b);

            // 如果右操作数不是WAngle类型,抛出异常
            throw new LuaException(
                "Attempted to call WAngle.Add(WAngle, WAngle) with invalid arguments " +
                $"({left.WrappedClrType().Name}, {right.WrappedClrType().Name})");
        }

        /// <summary>
        /// 实现Lua减法操作的接口方法
        /// </summary>
        public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
        {
            // 尝试获取左操作数
            if (!left.TryGetClrValue(out WAngle a))
                throw new LuaException(
                    "Attempted to call WAngle.Subtract(WAngle, WAngle) with invalid arguments " +
                    $"({left.WrappedClrType().Name}, {right.WrappedClrType().Name})");

            // 尝试获取右操作数并执行减法
            if (right.TryGetClrValue(out WAngle b))
                return new LuaCustomClrObject(a - b);

            // 如果右操作数不是WAngle类型,抛出异常
            throw new LuaException(
                "Attempted to call WAngle.Subtract(WAngle, WAngle) with invalid arguments " +
                $"({left.WrappedClrType().Name}, {right.WrappedClrType().Name})");
        }

        /// <summary>
        /// 实现Lua相等比较的接口方法
        /// </summary>
        public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
        {
            // 尝试获取两个操作数,如果任一不是WAngle类型,返回false
            if (!left.TryGetClrValue(out WAngle a) || !right.TryGetClrValue(out WAngle b))
                return false;

            // 比较两个WAngle是否相等
            return a == b;
        }

        /// <summary>
        /// 实现Lua表访问的接口方法,允许通过angle["Angle"]访问角度值
        /// </summary>
        public LuaValue this[LuaRuntime runtime, LuaValue key]
        {
            get
            {
                switch (key.ToString())
                {
                    case "Angle": return Angle;
                    default: throw new LuaException($"WAngle does not define a member '{key}'");
                }
            }

            // 设置值时抛出异常,因为WAngle是只读的
            set => throw new LuaException("WAngle is read-only. Use Angle.New to create a new value");
        }

        /// <summary>
        /// 实现Lua转字符串的接口方法
        /// </summary>
        public LuaValue ToString(LuaRuntime runtime) => ToString();

        #endregion
    }
}

WAngle结构的逻辑解读

WAngle是OpenRA游戏引擎中用于表示角度的结构,采用了一种特殊的角度表示方法:1024单位表示360度。这种表示方法的主要优势是:

  1. 可以使用整数进行角度计算,避免浮点数精度问题
  2. 角度值可以用0-1023的整数表示,便于存储和计算
  3. 通过预计算的三角函数表,可以快速获取角度的三角函数值

WAngle结构实现了多种功能:

  • 基本的角度表示和转换
  • 三角函数计算(正弦、余弦、正切)
  • 反三角函数计算(反正弦、反余弦、反正切)
  • 角度间的线性插值
  • 与Lua脚本的交互接口

流程图

三角函数计算流程

0-256
257-512
>512
正弦计算流程
返回CosAngle-256
计算Sin
余弦计算流程
角度<=256
计算Cos
返回CosineTableAngle
角度<=512
返回-CosineTable512-Angle
返回-CosAngle-512
输入WAngle
角度范围
直接使用查找表
使用对称性计算
递归计算
返回结果

反三角函数计算流程

ClosestCosineIndex二分查找
初始化上下边界
输入值
上边界!=下边界-1?
计算中间索引
表值==目标值?
返回索引
表值<目标值?
调整下边界
调整上边界
返回误差最小的索引
输入值
值在-1024到1024范围内?
抛出异常
计算绝对值
使用ClosestCosineIndex查找最接近的角度
原值为负?
根据函数调整角度
根据函数调整角度
返回WAngle

ArcTan计算流程

输入x,y
y==0?
返回x>=0?0:512
x==0?
返回Sign_y*256
计算x和y
初始化最佳值和角度
遍历正切表寻找最佳匹配
根据x,y符号确定象限
返回WAngle

特殊语法与技巧解读

  1. 只读结构体

    public readonly struct WAngle
    

    使用readonly struct表示该结构体是不可变的,有助于线程安全和性能优化。

  2. 表达式体成员

    public int AngleSquared => Angle * Angle;
    public int Facing => Angle / 4;
    

    使用=>语法简化属性的定义,这是C#6.0引入的表达式体成员语法。

  3. 静态只读字段

    public static readonly WAngle Zero = new(0);
    static readonly int[] CosineTable = { ... };
    

    使用静态只读字段存储常量和预计算表,提高性能。

  4. 运算符重载

    public static WAngle operator +(WAngle a, WAngle b) { return new WAngle(a.Angle + b.Angle); }
    

    重载运算符使WAngle可以像基本类型一样使用算术运算符。

  5. 接口实现

    public readonly struct WAngle : IEquatable<WAngle>, IScriptBindable, ...
    

    实现多个接口使WAngle可以与.NET框架和Lua脚本系统无缝集成。

  6. 模数运算与规范化

    Angle = a % 1024;
    if (Angle < 0)
        Angle += 1024;
    

    确保角度值始终在0-1023范围内,处理负数和大于1023的值。

  7. 查表优化

    return CosineTable[Angle];
    

    使用预计算的表格代替实时计算三角函数,大大提高性能。

  8. 递归计算

    return -new WAngle(Angle - 512).Cos();
    

    利用三角函数的对称性和周期性,通过递归减少表的大小。

  9. 二分查找算法

    static int ClosestCosineIndex(int value) { ... }
    

    在反三角函数计算中使用二分查找提高效率。

  10. 字符串插值

    throw new LuaException($"WAngle does not define a member '{key}'");
    

    使用C#6.0引入的字符串插值功能简化字符串格式化。

  11. out参数与模式匹配

    if (!left.TryGetClrValue(out WAngle a))
    

    结合out参数和TryGetXXX模式简化错误处理。

  12. Lambda表达式

    public LuaValue ToString(LuaRuntime runtime) => ToString();
    

    使用Lambda表达式简化单行方法的定义。

  13. 区域标记

    #region Scripting interface
    // ...
    #endregion
    

    使用#region/#endregion组织代码,提高可读性。

  14. 预计算表与精度控制

    static readonly int[] CosineTable = { ... };
    static readonly int[] TanTable = { ... };
    

    使用整数表示三角函数值,将[-1,1]映射到[-1024,1024],提高精度和性能。

  15. 特殊情况优化

    if (y == 0)
        return new WAngle(x >= 0 ? 0 : 512);
    

    对常见特殊情况进行单独处理,提高性能。

这个WAngle结构展示了游戏开发中处理角度和三角函数的高效方法,通过整数表示、预计算表和对称性计算,在保持精度的同时实现了高性能。

计算机算法的树结构有哪些种请分别列举
如何使用Python开发一个度假租赁市场平台。
车载系统软件工程师如何确保车载系统的网络安全(防止黑客攻击)
量化交易系统中+如何处理市场操纵和内幕交易的风险?
使用 Python 和 Gretel.ai 生成合成位置数据
3D建模完成以后,如何用编程语言控制这些模型的展示和动画
python web应用开发神器 入门十一
python编写一个简单神经网络计算代码带有反向传播,不用任何框架
python 的statsmodels库如何使用,有哪些功能
python分布式系统技术集成的应用
python的mailcap库如何使用
c#视觉应用开发中如何在C#中进行图像饱和度调整?
opencv多线程视频处理示例
c#视觉应用开发中如何在C#中进行图像去干扰?
python 的pytorch库介绍
量化交易策略 行业板块选择
python如何判断一个文件是否已经写入完成
NI-Motion控制两轴舞台按照预设的路径进行移动来实现光栅扫描C语言示例代码
人工智能开源库有哪些
C#进行串口应用开发如何快速验证和调试串口通信的程序
量化交易系统如何获取实时市场数据?
python如何自动生成流程图
如何控制多部手机进行同时测试,俗称群控
车载系统软件工程师如何管理车载系统的固件更新(OTA)
智能农业设备软件工程师如何实现农业机器人路径规划
python web应用开发神器 入门十六
量化交易系统中+如何应用机器学习进行预测和决策?
python的torchversion库的介绍
python如何开发一个计算中国象棋下子优势的算法
在搜索引擎如百度上搜索合法软件(如Notepad++和VNote)的用户正成为恶意广告和伪造链接的目标
python如何创建内存视图
microPython的源码解析之 nlrx86.c
microPython的源码解析之 objset.c
python如何能简单快速的加载配置文件
Python如何把一个列表按照一定数量均匀的切片
__pragma(warning(push)) 是什么意思
量化交易系统中+如何避免策略的过拟合?
C#进行串口应用开发如何避免串口通信因缓冲区溢出丢失数据
C#进行串口应用开发如何实现不同厂家串口设备的标准兼容接口
量化交易策略 随机游走
Python的pkg_resources库介绍
车载系统软件工程师如何处理车载系统的硬件抽象层和驱动程序开发
C#进行串口应用开发如何实现串口通信性能和稳定性的优化
Union Investment如何利用Python和机器学习(ML)技术来改进其投资流程
C#进行串口应用开发如何实现串口的即时通信
自制脚本语言,必知必会BNF 的语法描述
量化交易系统中如何处理分布式计算中的数据同步问题?
量化交易系统中+如何设计和实现高频交易算法?
C#进行串口应用开发如何设置串口的波特率
智能农业设备软件工程师如何集成和管理农业物联网(IoT)平台
NI-Motion如何配置运动控制器上的一个相对位置断点,并通过 RTI 线路(Real-Time System Interface)发送该断点信号的C语言示例代码
c#视觉应用开发中如何在C#中进行图像颜色空间转换?
如何应聘普通测试工程师
c#视觉应用开发中如何在C#中进行视频处理和实时流媒体处理?
车载系统软件工程师如何集成车载系统与ADAS(高级驾驶辅助系统)
c#视觉应用开发中如何在C#中进行图像压缩和解压缩?
智能农业设备软件工程师如何处理设备的通信协议栈开发
python web应用开发神器 入门五
linux的如何管理网络端口及访问权限,与window比较区别在哪儿
python的PyVista库如何使用
microPython的源码解析之 moderrno.c
NI-Motion实现控制器的球面弧线运动控制功能C语言代码
c#视觉应用开发中如何在C#中进行图像模糊处理?
量化交易系统中+如何处理算法交易中的竞价和撮合机制?
量化交易策略 技术指标
microPython的源码解析之 objtype.c
python如何分析 图的最短路径
python的NLTK库如何使用
linux如何开发一些自定义命令
python的ftplib库如何使用
C#进行串口应用开发如何设置串口的数据位、停止位和校验位
python的unittest库如何使用功能
搞科研,不能吊在matlab这一棵树上.还有其他好用的开源软件.
矩阵运算思维如何理解
量化交易系统中+如何进行代码的优化和重构?
C#进行串口应用开发如何通过串口实现设备固件的远程升级
C#进行串口应用开发如何快速排查串口通信故障的原因
c#视觉应用开发中如何在C#中进行图像分块处理?
openai的API使用Embeddings文本分类的示例
DeepMind的开源库Sonnet如何使用
Milvus开源的向量相似度搜索引擎
量化交易系统中+如何处理历史数据的准确性和一致性?
Python程序记日志竟如此简单
量化交易系统中如何处理分布式系统中的数据一致性问题?
量化交易系统中+如何处理交易所的延迟和网络延迟?
morris蠕虫病毒
python 如何将传统关系数据库的数据导入 Hadoop
microPython的源码解析之 sequence.c
NI-Motion实现基于速度的执行运动 的C语言代码示例
NI-Motion运动控制器获取固件定时采样的位置和速度数据的C语言示例程序
Python如何调用pygame库来启动摄像头捕获图像并显示
量化交易系统中+如何优化系统的内存和CPU使用?
Python的高性能web框架库Tornado
C#进行串口应用开发如何优化串口通信的实时性与吞吐量
智能农业设备软件工程师如何实现农业设备的智能灌溉控制
python的Godot Engine库如何安装使用以及功能和用途
Python的opencv库进行物体跟踪
c#任务并行性进行多线程编程
C#进行串口应用开发如何显示和处理串口调试信息
量化交易策略 做多做空策略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值