I don’t under stand how fighting games are able to be made and be deterministic at the same time. For example how am I supposed to keep track of the players position without using float!? Let alone accelerate them at a fixed reasonable speed? I don’t know how I would do this and I really need help. I used gemini to help me write a vector3 fixed point class but I’m still not confident that this is good enough to use it.
using System;
using Godot;
namespace ProjectKazen.Framework.Util;
public class FixedPoint
{
protected const int Shift = 16;
protected const long One = 1L << Shift;
public static float LongToFloat(long Value) => (float)Value / One;
public static long FloatToLong(float Value) => (long)MathF.Floor(Value * One);
public class Vector3 : FixedPoint
{
public readonly long RawX;
public readonly long RawY;
public readonly long RawZ;
public Godot.Vector3 ToFloatData => new(LongToFloat(RawX),LongToFloat(RawY),LongToFloat(RawZ));
//Convert the raw into deterministic values by shifting left because ints have a less bytes than longs
public int X => (int)(RawX >> Shift);
public int Y => (int)(RawY >> Shift);
public int Z => (int)(RawZ >> Shift);
public Vector3I FixedData => new(X, Y, Z);
public Vector3(int X, int Y, int Z)
{
//Shift XYZ by 16 to the left and convert to long because the number the ints can represent need to be bigger
RawX = (long)X << Shift;
RawY = (long)Y << Shift;
RawZ = (long)Z << Shift;
}
public Vector3(long X, long Y, long Z)
{
RawX = X;
RawY = Y;
RawZ = Z;
}
public static implicit operator Vector3I(Vector3 Self) => Self.FixedData;
// Math operators cause the IDE can't do it for us
public static Vector3I operator +(Vector3 Self, Vector3I Other) => new(Self.X + Other.X, Self.Y + Other.Y, Self.Z + Other.Z);
public static Vector3I operator -(Vector3 Self, Vector3I Other) => new(Self.X - Other.X, Self.Y - Other.Y, Self.Z - Other.Z);
public static Vector3 operator +(Vector3 Self, Vector3 Other) => new(Self.RawX + Other.RawX, Self.RawY + Other.RawY, Self.RawZ + Other.RawZ);
public static Vector3 operator -(Vector3 Self, Vector3 Other) => new(Self.RawX - Other.RawX, Self.RawY - Other.RawY, Self.RawZ - Other.RawZ);
public static Vector3 operator /(Vector3 Self, float Divisor)
{
long ConvertedDivisor = FloatToLong(Divisor); //Convert Divisor to long so it is deterministic
if (ConvertedDivisor == 0) return Self;
//Shift Left before the division for all of the raw values
return new((Self.RawX << Shift) / ConvertedDivisor, (Self.RawY << Shift) / ConvertedDivisor, (Self.RawZ << Shift) / ConvertedDivisor);
}
public static Vector3 operator *(Vector3 Self, float Multiplier)
{
long ConvertedMultiplier = FloatToLong(Multiplier);
//Multiply before the multiplication for all raw values
return new((Self.RawX * ConvertedMultiplier) >> Shift, (Self.RawY * ConvertedMultiplier) >> Shift, (Self.RawZ * ConvertedMultiplier) >> Shift);
}
public long MagnitudeSquared()
{
return (RawX * RawX) + (RawY * RawY) + (RawZ * RawZ);
}
public Vector3 MoveToward(Vector3 to, float delta)
{
Vector3 vector = this;
Vector3 vector2 = to - vector;
float num = vector2.MagnitudeSquared();
if (num <= delta || num < 1E-06f)
{
return to;
}
return vector + vector2 / num * FloatToLong(delta);
}
}
}
I tried commenting it my self so I could understand it but I gave up trying to understand why I shift during specific math operations. If any one could help me or point me to a source I would be very greatfull! From what I can understand is that longs have a wider range of numbers because it’s 64 bits and we use that instead of ints because it’s 32 and I think the idea behind this is to use longs to and bit shifting to make it left it makes the number bigger. But shifting it right makes it smaller. Also I’m am supposed to shifting during specific math operations but I also don’t understand why we shift before or after certian parts of it. Specificly for multiplication and division. Other than that I don’t know what I’m doing