第7章 标准库和游戏实例

7.1 数学库

数学计算在游戏开发中扮演着极其重要的角色,从简单的分数计算到复杂的物理模拟,无处不需要数学函数。Lua提供了基本的数学库,而UE5.2则拥有更加强大和游戏导向的数学功能。

7.1.1 基本数学函数

Lua的数学库提供了基本的数学函数:

lua

-- 基本数学常量
print(math.pi)       -- 3.1415926535898
print(math.huge)     -- 正无穷大

-- 取整函数
print(math.floor(3.7))  -- 3 (向下取整)
print(math.ceil(3.7))   -- 4 (向上取整)

-- 三角函数 (参数使用弧度)
print(math.sin(math.pi/2))  -- 1.0
print(math.cos(math.pi))    -- -1.0
print(math.tan(math.pi/4))  -- 约为1.0

-- 指数和对数
print(math.exp(1))      -- e^1, 约为2.718
print(math.log(10))     -- ln(10), 约为2.302
print(math.log(100, 10)) -- log10(100), 等于2

-- 幂运算
print(math.pow(2, 3))   -- 2^3, 等于8

-- 平方根
print(math.sqrt(16))    -- 4

-- 随机数
math.randomseed(os.time()) -- 设置随机数种子
print(math.random())     -- 0到1之间的随机数
print(math.random(10))   -- 1到10之间的随机整数
print(math.random(5, 10)) -- 5到10之间的随机整数

-- 最大值和最小值
print(math.max(5, 10, 3)) -- 10
print(math.min(5, 10, 3)) -- 3

-- 绝对值
print(math.abs(-10))    -- 10

UE5.2中的数学函数:

UE5.2提供了强大的数学库,主要通过FMath命名空间提供:

C++数学函数示例:

cpp

// 数学常量
float Pi = PI;              // 3.1415926535897932
float HalfPi = PI_2;        // PI/2
float TwoPi = PI * 2.0f;    // 2*PI
float InvPi = 1.0f / PI;    // 1/PI

// 取整函数
int32 FloorValue = FMath::FloorToInt(3.7f);   // 3
int32 CeilValue = FMath::CeilToInt(3.7f);     // 4
int32 RoundValue = FMath::RoundToInt(3.7f);   // 4
float TruncValue = FMath::TruncToFloat(3.7f); // 3.0

// 三角函数(参数使用弧度)
float SinValue = FMath::Sin(PI * 0.5f);      // 1.0
float CosValue = FMath::Cos(PI);             // -1.0
float TanValue = FMath::Tan(PI * 0.25f);     // 约为1.0

// 角度与弧度转换
float Degrees = 45.0f;
float Radians = FMath::DegreesToRadians(Degrees); // 约为0.785
float BackToDegrees = FMath::RadiansToDegrees(Radians); // 45.0

// 指数和对数
float ExpValue = FMath::Exp(1.0f);           // e^1, 约为2.718
float LogValue = FMath::Loge(10.0f);         // ln(10), 约为2.302
float Log10Value = FMath::Log10(100.0f);     // log10(100), 等于2

// 幂运算
float PowValue = FMath::Pow(2.0f, 3.0f);     // 2^3, 等于8

// 平方根
float SqrtValue = FMath::Sqrt(16.0f);        // 4.0
float InvSqrtValue = FMath::InvSqrt(16.0f);  // 1/4 = 0.25

// 随机数
int32 Seed = FMath::Rand();                  // 获取随机数
FMath::SRandInit(FPlatformTime::Cycles());   // 初始化随机数种子
float RandomFloat = FMath::FRand();          // 0到1之间的随机数
int32 RandomInt = FMath::RandRange(5, 10);   // 5到10之间的随机整数
float RandomFloatRange = FMath::FRandRange(5.0f, 10.0f); // 5到10之间的随机浮点数

// 最大值和最小值
float MaxValue = FMath::Max(5.0f, 10.0f);    // 10.0
float MinValue = FMath::Min(5.0f, 10.0f);    // 5.0
int32 ClampValue = FMath::Clamp(15, 0, 10);  // 10 (限制在0到10之间)

// 绝对值
float AbsValue = FMath::Abs(-10.0f);         // 10.0

// 线性插值
float LerpValue = FMath::Lerp(0.0f, 10.0f, 0.5f); // 5.0 (50%插值)

// 检查浮点数是否接近
bool bIsNearlyEqual = FMath::IsNearlyEqual(0.1f + 0.2f, 0.3f); // 通常为true
bool bIsNearlyZero = FMath::IsNearlyZero(0.00001f);            // 检查是否接近零

// 计算交叉乘积
float CrossProduct2D = FMath::CrossProduct2D(
    FVector2D(1.0f, 0.0f), 
    FVector2D(0.0f, 1.0f)
); // 1.0

蓝图数学函数:

UE5.2蓝图系统提供了丰富的数学节点:

UE5蓝图数学函数

在蓝图中创建简单的随机数生成器:

  1. 创建一个函数,接收最小值和最大值参数
  2. 使用"Random Float in Range"节点生成随机数
  3. 返回生成的随机值

7.1.2 向量运算

游戏开发中,向量运算是物理模拟、空间计算和运动控制的基础:

Lua中模拟向量运算:

lua

-- 自定义简单的向量库
local vector = {}

function vector.new(x, y, z)
    return {x = x or 0, y = y or 0, z = z or 0}
end

-- 向量加法
function vector.add(a, b)
    return vector.new(a.x + b.x, a.y + b.y, a.z + b.z)
end

-- 向量减法
function vector.sub(a, b)
    return vector.new(a.x - b.x, a.y - b.y, a.z - b.z)
end

-- 向量点乘
function vector.dot(a, b)
    return a.x * b.x + a.y * b.y + a.z * b.z
end

-- 向量叉乘
function vector.cross(a, b)
    return vector.new(
        a.y * b.z - a.z * b.y,
        a.z * b.x - a.x * b.z,
        a.x * b.y - a.y * b.x
    )
end

-- 向量长度
function vector.length(a)
    return math.sqrt(a.x * a.x + a.y * a.y + a.z * a.z)
end

-- 向量单位化
function vector.normalize(a)
    local len = vector.length(a)
    if len > 0 then
        return vector.new(a.x / len, a.y / len, a.z / len)
    else
        return vector.new(0, 0, 0)
    end
end

-- 使用向量
local v1 = vector.new(1, 2, 3)
local v2 = vector.new(4, 5, 6)
local v3 = vector.add(v1, v2)      -- (5, 7, 9)
local dotProduct = vector.dot(v1, v2)  -- 32
local crossProduct = vector.cross(v1, v2) -- (-3, 6, -3)
local length = vector.length(v1)    -- 约为3.74
local normalized = vector.normalize(v1)

UE5.2中的向量运算:

UE5.2提供了强大的向量类和操作:

C++向量运算:

cpp

// 创建向量
FVector Vector1(1.0f, 2.0f, 3.0f);
FVector Vector2(4.0f, 5.0f, 6.0f);

// 向量加法
FVector Sum = Vector1 + Vector2;         // (5, 7, 9)

// 向量减法
FVector Difference = Vector1 - Vector2;  // (-3, -3, -3)

// 标量乘法
FVector Scaled = Vector1 * 2.0f;         // (2, 4, 6)

// 点乘
float DotProduct = FVector::DotProduct(Vector1, Vector2); // 32

// 叉乘
FVector CrossProduct = FVector::CrossProduct(Vector1, Vector2); // (-3, 6, -3)

// 向量长度
float Length = Vector1.Size();           // 约为3.74
float SqrLength = Vector1.SizeSquared(); // 约为14

// 单位化向量
FVector Normalized = Vector1.GetSafeNormal(); // 返回单位向量
Vector1.Normalize();                     // 原地单位化

// 向量夹角(弧度)
float Angle = FMath::Acos(
    FVector::DotProduct(Vector1.GetSafeNormal(), Vector2.GetSafeNormal())
);

// 向量投影
FVector Projection = Vector1.ProjectOnTo(Vector2);

// 向量插值
FVector Lerp = FMath::Lerp(Vector1, Vector2, 0.5f); // 50%插值

// 向量反射
FVector Normal = FVector(0.0f, 0.0f, 1.0f); // 向上的法线
FVector Incident = FVector(1.0f, 1.0f, -1.0f);
FVector Reflected = FMath::GetReflectionVector(Incident, Normal);

// 2D向量
FVector2D Vec2D(1.0f, 2.0f);
float Length2D = Vec2D.Length();        // 约为2.24

// 4D向量(常用于四元数和齐次坐标)
FVector4 Vec4D(1.0f, 2.0f, 3.0f, 1.0f);

// 使用向量进行游戏计算
void AMoveComponent::MoveAlongDirection(FVector Direction, float Speed)
{
    // 确保方向是单位向量
    Direction.Normalize();
    
    // 计算移动增量
    FVector DeltaMove = Direction * Speed * GetWorld()->DeltaTimeSeconds;
    
    // 应用移动
    GetOwner()->AddActorWorldOffset(DeltaMove, true);
}

蓝图向量运算:

蓝图系统提供了全面的向量操作节点:

UE5蓝图向量运算

在蓝图中创建角色朝向目标的功能:

  1. 获取角色当前位置和目标位置
  2. 使用"Subtract"节点计算从角色到目标的向量
  3. 使用"Normalize"节点获取单位方向向量
  4. 使用"Make Rotation from X"节点创建朝向该方向的旋转
  5. 使用"Set Actor Rotation"设置角色朝向

7.1.3 矩阵和四元数

矩阵和四元数在3D游戏中用于表示和操作旋转、缩放和位置变换:

Lua中模拟简单矩阵操作:

lua

-- 简单的4x4矩阵实现(仅示例)
local matrix = {}

function matrix.new()
    -- 创建单位矩阵
    return {
        {1, 0, 0, 0},
        {0, 1, 0, 0},
        {0, 0, 1, 0},
        {0, 0, 0, 1}
    }
end

-- 矩阵乘法(简化版,仅示意)
function matrix.multiply(a, b)
    local result = matrix.new()
    for i = 1, 4 do
        for j = 1, 4 do
            result[i][j] = 0
            for k = 1, 4 do
                result[i][j] = result[i][j] + a[i][k] * b[k][j]
            end
        end
    end
    return result
end

-- 创建平移矩阵
function matrix.translation(x, y, z)
    local m = matrix.new()
    m[4][1] = x
    m[4][2] = y
    m[4][3] = z
    return m
end

-- 简单的四元数实现(仅示例)
local quaternion = {}

function quaternion.new(x, y, z, w)
    return {x = x or 0, y = y or 0, z = z or 0, w = w or 1}
end

-- 从欧拉角创建四元数(简化版)
function quaternion.fromEuler(pitch, yaw, roll)
    -- 实际实现需要复杂的数学计算
    -- 这里仅为示意
    return quaternion.new(pitch/10, yaw/10, roll/10, 1)
end

UE5.2中的矩阵和四元数:

UE5.2提供了高效的矩阵和四元数类:

C++矩阵和四元数:

cpp

// 矩阵操作
// 创建变换矩阵
FTransform Transform;
Transform.SetLocation(FVector(100.0f, 200.0f, 300.0f));
Transform.SetRotation(FQuat(FRotator(0.0f, 90.0f, 0.0f)));
Transform.SetScale3D(FVector(2.0f, 2.0f, 2.0f));

// 获取矩阵
FMatrix Matrix = Transform.ToMatrixWithScale();

// 矩阵乘法
FMatrix Matrix1 = FRotationMatrix::Make(FRotator(0.0f, 90.0f, 0.0f));
FMatrix Matrix2 = FTranslationMatrix(FVector(100.0f, 0.0f, 0.0f));
FMatrix Combined = Matrix1 * Matrix2; // 先旋转后平移

// 从矩阵中提取变换信息
FVector Translation;
FQuat Rotation;
FVector Scale;
Combined.DecomposeNoScale(Translation, Rotation);
Translation = Combined.GetOrigin();
Scale = Combined.GetScaleVector();

// 矩阵变换点
FVector Point(1.0f, 0.0f, 0.0f);
FVector TransformedPoint = Combined.TransformPosition(Point);

// 矩阵变换向量(不包括平移)
FVector Direction(1.0f, 0.0f, 0.0f);
FVector TransformedDirection = Combined.TransformVector(Direction);

// 四元数操作
// 创建四元数
FQuat Quaternion1 = FQuat::Identity; // 单位四元数
FQuat Quaternion2 = FQuat(FRotator(0.0f, 90.0f, 0.0f)); // 从旋转器创建

// 四元数乘法(组合旋转)
FQuat CombinedQuat = Quaternion1 * Quaternion2;

// 四元数旋转向量
FVector RotatedVector = Quaternion2.RotateVector(FVector(1.0f, 0.0f, 0.0f));

// 四元数插值(平滑旋转)
FQuat InterpolatedQuat = FQuat::Slerp(Quaternion1, Quaternion2, 0.5f);

// 从四元数获取旋转角度(弧度)
float Angle;
FVector Axis;
Quaternion2.ToAxisAndAngle(Axis, Angle);

// 四元数逆
FQuat InverseQuat = Quaternion2.Inverse();

// 在游戏中应用旋转
void ASpaceshipController::RotateShip(FRotator TargetRotation, float InterpSpeed)
{
    FQuat CurrentQuat = GetActorQuat();
    FQuat TargetQuat = TargetRotation.Quaternion();
    
    // 平滑插值到目标旋转
    FQuat NewQuat = FQuat::Slerp(CurrentQuat, TargetQuat, InterpSpeed * GetWorld()->DeltaTimeSeconds);
    
    // 应用旋转
    SetActorRotation(NewQuat);
}

蓝图矩阵和四元数:

蓝图中也可以使用矩阵和四元数:

UE5蓝图矩阵和四元数

在蓝图中创建平滑旋转相机的功能:

  1. 获取当前相机旋转(四元数)
  2. 获取目标旋转(四元数)
  3. 使用"Quaternion Slerp"节点进行平滑插值
  4. 应用插值后的旋转到相机

7.1.4 应用:弹道轨迹计算

结合数学库功能创建实用的游戏功能:

Lua中计算抛物线轨迹:

lua

function calculateProjectilePath(startPos, velocity, gravity, timeStep, steps)
    local path = {}
    local pos = {x = startPos.x, y = startPos.y, z = startPos.z}
    local vel = {x = velocity.x, y = velocity.y, z = velocity.z}
    
    for i = 1, steps do
        -- 更新位置
        pos.x = pos.x + vel.x * timeStep
        pos.y = pos.y + vel.y * timeStep
        pos.z = pos.z + vel.z * timeStep
        
        -- 更新速度(仅受重力影响)
        vel.z = vel.z + gravity * timeStep
        
        -- 记录路径点
        table.insert(path, {x = pos.x, y = pos.y, z = pos.z})
    end
    
    return path
end

-- 计算投射物落点
function calculateLandingPoint(startPos, velocity, gravity)
    -- 解二次方程:z = startPos.z + velocity.z * t + 0.5 * gravity * t^2 = 0
    -- 假设地面高度为0
    local a = 0.5 * gravity
    local b = velocity.z
    local c = startPos.z
    
    -- 计算正时间根
    local discriminant = b * b - 4 * a * c
    if discriminant < 0 then
        return nil -- 无解,不会落地
    end
    
    local t = (-b + math.sqrt(discriminant)) / (2 * a)
    if t < 0 then
        return nil -- 负时间,不合理
    end
    
    -- 计算落点
    return {
        x = startPos.x + velocity.x * t,
        y = startPos.y + velocity.y * t,
        z = 0
    }
end

UE5.2中实现弹道轨迹:

UE5.2提供了多种方式实现弹道计算:

C++弹道轨迹计算:

cpp

// 计算抛物线轨迹点
TArray<FVector> UProjectileHelper::CalculateProjectilePath(
    const FVector& StartPosition,
    const FVector& InitialVelocity,
    float Gravity,
    float TimeStep,
    int32 Steps)
{
    TArray<FVector> Path;
    Path.Reserve(Steps);
    
    FVector Position = StartPosition;
    FVector Velocity = InitialVelocity;
    
    for (int32 i = 0; i < Steps; i++)
    {
        // 添加当前位置到路径
        Path.Add(Position);
        
        // 更新位置
        Position += Velocity * TimeStep;
        
        // 更新速度(仅受重力影响)
        Velocity.Z += Gravity * TimeStep;
    }
    
    return Pat
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小宝哥Code

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

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

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

打赏作者

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

抵扣说明:

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

余额充值