A C# library of utility types, math helpers, and game-systems primitives for .NET 10. Primarily aimed at game development, with a focus on performance and zero unnecessary allocations.
dotnet add package JST.GameUtils
- Contributing
- Math
- Vector extensions
- Geometry
- Collections
- Entity / Game systems
- Animation
- Procedural generation
- Types
- Extensions
- Terminal
- Performance is a key goal. Avoid unnecessary allocations, prefer structs for small types, and consider cache locality.
- Add XML documentation comments to all public members for better IntelliSense support.
- Add tests for all public APIs, covering edge cases and typical usage patterns.
- Update the README with usage examples for any new features or types added.
MathFExt.Lerp(0f,10f,0.5f);// 5fMathFExt.InverseLerp(0f,10f,5f);// 0.5fMathFExt.Smoothstep(0f,1f,0.5f);MathFExt.Smootherstep(0f,1f,0.5f);MathFExt.Remap(value,0f,1f,-1f,1f);MathFExt.RemapClamped(value,0f,1f,-1f,1f);MathFExt.Wrap(value,min,max);MathFExt.Normalize(value,min,max);MathFExt.PingPong(t,length);MathFExt.AngleDifference(fromAngle,toAngle);MathFExt.ToRadians(degrees);MathFExt.ToDegrees(radians);Constants: DEGREES_PER_RADIANS, RADIANS_PER_DEGREE, HALF_PI.
MathExt.RandomFloat();// [0, 1)MathExt.RandomFloat(min,max);MathExt.RandomInt(min,max);MathExt.RandomBool();MathExt.RandomGaussian(mean,stddev);MathExt.RandomInCircle(radius);// random point inside circleMathExt.RandomOnCircle(radius);// random point on circle edgeAll functions take float t in [0, 1] and return a float.
Available families: Sine, Quad, Cubic, Quart, Quint, Expo, Circ, Back, Elastic, Bounce.
Each has In, Out, and InOut variants.
floatt=Easing.CubicInOut(progress);floatt=Easing.BounceOut(progress);floatt=Easing.ElasticIn(progress);Vector2point=Bezier.Quadratic(p0,p1,p2,t);Vector2point=Bezier.Cubic(p0,p1,p2,p3,t);Vector2tangent=Bezier.CubicTangent(p0,p1,p2,p3,t);varpath=newBezierPath();path.AddSegment(p0,p1,p2,p3);Vector2point=path.GetPoint(t);// t over entire path [0, 1]Vector2tangent=path.GetTangent(t);Unlike Bézier curves, Catmull-Rom splines pass through every control point — ideal for camera paths, patrol routes, and cutscenes.
// Single segmentVector2point=CatmullRom.Sample(p0,p1,p2,p3,t);Vector2tangent=CatmullRom.Tangent(p0,p1,p2,p3,t);// Multi-point pathvarpath=newCatmullRomPath().AddPoint(newVector2(0,0)).AddPoint(newVector2(5,3)).AddPoint(newVector2(10,0)).SetLooping(false);Vector2pos=path.GetPoint(0.5f);// midpoint of pathVector2dir=path.GetTangent(0.5f);varbag=newShuffleBag<string>();bag.Add("common",weight:10);bag.Add("rare",weight:1);stringitem=bag.Next();bag.Reset();vec.Rotate(radians);vec.Rotate(radians,origin);vec.Add(scalar);vec.Add(x,y);vec.Floor();vec.Ceil();vec.Round();vec.Perpendicular();vec.WithX(x);vec.WithY(y);vec.IsZero();vec.ToAngle();vec.AngleTowards(target);vec.AngleBetween(other);vec.GetDirection(target);vec.MoveTowards(target,maxDelta);vec.ToVector3(z);vec.Deconstruct(outx,outy);// On IEnumerable<Vector2>:vectors.Midpoint();vectors.Sort(clockwise:true);vectors.Sort(center,clockwise:true);Includes WithX, WithY, WithZ, ToVector2, Deconstruct, and other component helpers.
All geometry types are in GameUtils.Types.Geometry.
varbox=newAABB(min,max);box.Contains(point);box.Intersects(other);// AABB, Line, Circle, Polygon2D, Vector2[]box.Intersects(line,outintersectionPoints);varcircle=newCircle(center,radius);circle.Contains(point);circle.Intersects(aabb);// AABB, Line, Circle, Polygon2Dcircle.RadiusSquared;// cached, avoids sqrt in hot pathsvarline=newLine(start,end);varline=Line.FromAngle(origin,angle,length);line.Length; line.Midpoint;line.NormalA; line.NormalB;line.Intersects(other);// Line, AABB, Circle, Polygon2Dline.Cast(maxDistance,shapes);// first intersection along rayvarray=newRay2D(origin,direction);ray.At(distance);ray.Intersects(line,outt,outpoint);ray.Intersects(circle,outt,outpoint);ray.Intersects(aabb,outt,outpoint);varpoly=newPolygon2D(vertices);// auto-sorts CCWpoly.Vertices; poly.Edges;poly.Normals;poly.Contains(point);poly.Intersects(aabb);poly.TranslateBy(delta);varquad=newQuad(topLeft,topRight,bottomLeft,bottomRight);quad.Intersects(line,outpoint);quad[index];// corner by index 0–3vargrid=newGrid<int>(width,height);grid[x,y]=1;grid[vector2]=1;grid.TryGet(x,y,outvalue);grid.Fill(defaultValue);grid.Fill((x,y)=>ComputeValue(x,y));grid.IsInBounds(x,y);foreach(var(x,y,value)ingrid){ ...}Best for non-uniform object distributions (e.g. world objects, static geometry).
vartree=newQuadTree<Enemy>(worldBounds,capacity:8);tree.Insert(enemy,enemy.Position);tree.Query(camera.GetVisibleBounds());// IEnumerable<Enemy>tree.Query(explosionCenter,blastRadius);tree.Remove(enemy,enemy.Position);tree.Clear();Best for large numbers of uniformly distributed dynamic objects (bullets, particles, units). O(1) amortized insert and query.
varhash=newSpatialHash<Bullet>(cellSize:64f);hash.Insert(bullet,bullet.Position);hash.Query(region);// IEnumerable<Bullet>hash.Query(center,radius);hash.Remove(bullet,bullet.Position);hash.Clear();varbuffer=newRingBuffer<float>(capacity:64);buffer.Write(sample);floatvalue=buffer.Read();buffer.TryRead(outvalue);buffer.Peek();buffer.Snapshot();// copy of all current elementsThread-safe set types for use in multi-threaded game systems.
varfsm=newStateMachine<State>(State.Idle);fsm.AddTransition(State.Idle,State.Run,()=>speed>0);fsm.OnEnter(State.Run,()=>PlayAnim("run"));fsm.OnExit(State.Run,()=>StopAnim());fsm.Update();// evaluates transitions and fires callbacksfsm.ForceState(State.Dead);varpool=newObjectPool<Bullet>(()=>newBullet());varbullet=pool.Rent();// ... use bullet ...pool.Return(bullet);Zero-allocation on the dispatch path (copy-on-write snapshot rebuilt only on subscribe/unsubscribe).
varbus=newEventBus();bus.Subscribe<PlayerDiedEvent>(e =>HandleDeath(e));bus.Publish(newPlayerDiedEvent{Position=pos});bus.Unsubscribe<PlayerDiedEvent>(handler);bus.Clear<PlayerDiedEvent>();// Float tween with cubic ease-outvartween=Tween.Float(0f,100f,duration:1.5f,Easing.CubicOut);floatvalue=tween.Update(deltaTime);if(tween.IsComplete){ ...}tween.Reset();tween.Reverse();// Vector2 tweenvarmove=Tween.Vec2(start,end,2f,Easing.SineInOut);// Color tweenvarfade=Tween.Color(Color.White,Color.Transparent,0.5f);vartree=newBehaviorTree(newSelector(newSequence(newIsEnemyVisible(),newChaseEnemy()),newPatrol()));tree.Tick(context);Node types: Sequence, Selector, Inverter, Leaf (for custom logic).
varastar=newAStar<Vector2Int>();astar.AddEdge(newEdge<Vector2Int>(a,b,cost));astar.Solve(start,goal,(a,b)=>Vector2.Distance(a,b),outvarpath);vardijkstra=newDijkstra<string>();dijkstra.AddEdge(newEdge<string>("A","B",1.5f));dijkstra.Solve("A","D",outvarpath);GridSearch.BreadthFirstSearch(grid,start,isWalkable,outpath);GridSearch.FloodFill(grid,origin,condition,fillValue);varthinker=newThinker(interval:0.5f);thinker.OnThink=()=>UpdateAI();thinker.Update(deltaTime);classPhysicsLoop:FixedScheduler{publicPhysicsLoop():base(tickRate:60){}protectedoverridevoidUpdate()=>StepPhysics(1f/60f);}varloop=newPhysicsLoop();loop.Start();// ... later ...loop.Stop();varanim=newController(frameCount:10,isLooping:true,framesPerSecond:12);anim.OnFrameChanged= frame =>currentSprite=sprites[frame];anim.Play();anim.Update(deltaTime);vartween=newTweener(from:0f,to:100f,duration:1.5f,easingFunction:Ease.CubicOut);tween.OnComplete=()=>Console.WriteLine("Done!");tween.Update(deltaTime);Ease contains easing functions like Ease.ElasticOut, Ease.BounceIn, etc.
Offset contains math functions for offsetting animations (Jagged, Sine, Pulse, Triangle, Wobble).
varnoise=newPerlinNoise();// standard Ken Perlin tablevarnoise=newPerlinNoise(seed:42);// seeded / reproduciblefloatv=noise.Sample(x,y);// 2D, [0, 1]floatv=noise.Sample(x,y,z);// 3D, [-1, 1]floatv=noise.Fbm(x,y,octaves:6);// fractal Brownian motion, [0, 1]// Or use the shared default instance:PerlinNoise.Default.Sample(x,y);Grid<int>heightmap=Diamond.Create(size:257,// must be power-of-two + 1min:0,max:255,range:128f,nextRange: r =>r*0.5f,valueFactory:(avg,range)=>(int)(avg+Random.Shared.NextSingle()*range),seed:42);varc=newColor(r:1f,g:0f,b:0.5f,a:1f);varc=newColor(byter,byteg,byteb,bytea);Color.Lerp(a,b,t);Color.Red; Color.SkyBlue;// 140+ named CSS colors(Vector3)c;(Vector4)c;// explicit castsColor.FromRgba(0xFF8800FF);vargradient=newGradient();gradient.AddStop(0f,Color.Black).AddStop(0.5f,Color.Red).AddStop(1f,Color.White);Colorc=gradient.Evaluate(0.75f);varcam=newCamera2D{Position=pos,Zoom=1.5f,Rotation=0f};Vector2screen=cam.WorldToScreen(worldPos);Vector2world=cam.ScreenToWorld(screenPos);AABBvisible=cam.GetVisibleBounds();Low-level pixel buffers. Bitmap supports drawing primitives (Rectangle, Line, Circle) and writing BMP files. ImageData is a compact RGBA container.
list.GetRandom();list.Shuffle();list.WeightedRandom(item =>item.Weight);list.ForEach(item =>DoSomething(item));list.SelectWhere(item =>item.IsActive, item =>item.Value);list.ToIndex();// Dictionary<T, int>obj.Mutate(o =>o.X=5);// mutate and return same objectobj.Out(outvarlocal);// assign to local, keep chainobj.Curry(arg1);// partial applicationstr.TryGet(index,outcharc);str.Repeat(3);Ansi.WriteLine("Hello!",Ansi.Bold+Ansi.Foreground(Color.Cyan));Ansi.MoveTo(x:10,y:5);Ansi.Clear();Ansi.ClearLine();Progress.Bar(current,total,width:40);Progress.PercentComplete(current,total);Progress.Rate(itemsProcessed,elapsed);Progress.TimeRemaining(current,total,elapsed);