.NET Gaming Samples

Multiplayer Lobby (C#)

A quick start guide for creating a multiplayer lobby using Microsoft.AspNetCore.SignalR. The sample shows host discovery, player join/leave events, and chat messaging.


using Microsoft.AspNetCore.SignalR;
using System.Collections.Concurrent;

public class LobbyHub : Hub
{
    private static readonly ConcurrentDictionary<string, string> Users = new();

    public async Task Join(string playerName)
    {
        Users[Context.ConnectionId] = playerName;
        await Clients.All.SendAsync("PlayerJoined", playerName);
    }

    public async Task SendMessage(string message)
    {
        var name = Users[Context.ConnectionId];
        await Clients.All.SendAsync("ReceiveMessage", name, message);
    }

    public override async Task OnDisconnectedAsync(Exception? ex)
    {
        if (Users.TryRemove(Context.ConnectionId, out var name))
        {
            await Clients.All.SendAsync("PlayerLeft", name);
        }
        await base.OnDisconnectedAsync(ex);
    }
}
                

Run the project, navigate to /lobby, and open multiple browsers to test real‑time interaction.

Physics Engine Integration (C# + F#)

Demonstrates binding the Unity Physics engine to a .NET Core backend using F# for immutable data structures.


open Microsoft.FSharp.Collections
open UnityEngine

type Entity = {
    Id: Guid
    Position: Vector3
    Velocity: Vector3
}

let update (dt:float) (entities:Map<Guid,Entity>) =
    entities
    |> Map.map (fun _ e ->
        let newPos = e.Position + e.Velocity * float32 dt
        { e with Position = newPos })
                

Integrate with the server via gRPC to sync game state across clients.

AI Patrol Bot (C#)

A simple state‑machine AI that patrols between waypoints and chases the player when in sight.


using System.Collections.Generic;
using UnityEngine;

public class PatrolBot : MonoBehaviour
{
    public List<Transform> Waypoints;
    public float Speed = 3f;
    public float ChaseRange = 10f;
    private int _current = 0;
    private Transform _player;

    void Start() => _player = GameObject.FindWithTag("Player").transform;

    void Update()
    {
        var target = Vector3.Distance(transform.position, _player.position) < ChaseRange
                     ? _player
                     : Waypoints[_current];

        var step = Speed * Time.deltaTime;
        transform.position = Vector3.MoveTowards(transform.position, target.position, step);

        if (Vector3.Distance(transform.position, target.position) < 0.1f && target != _player)
            _current = (_current + 1) % Waypoints.Count;
    }
}
                

Attach the script to an NPC GameObject and assign waypoints in the inspector.

Procedural Terrain (C#)

Generates height‑maps using Perlin noise and creates a mesh at runtime.


using UnityEngine;

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class TerrainGenerator : MonoBehaviour
{
    public int Size = 256;
    public float Scale = 20f;
    public int Octaves = 4;
    public float Persistence = 0.5f;
    public float Lacunarity = 2f;

    void Start() => Generate();

    void Generate()
    {
        var mesh = new Mesh();
        var verts = new Vector3[Size * Size];
        var uvs = new Vector2[verts.Length];
        var tris = new int[(Size - 1) * (Size - 1) * 6];

        for (int y = 0; y < Size; y++)
        for (int x = 0; x < Size; x++)
        {
            float height = 0;
            float amplitude = 1;
            float frequency = 1;
            for (int i = 0; i < Octaves; i++)
            {
                float sampleX = x / Scale * frequency;
                float sampleY = y / Scale * frequency;
                height += Mathf.PerlinNoise(sampleX, sampleY) * amplitude;
                amplitude *= Persistence;
                frequency *= Lacunarity;
            }
            verts[y * Size + x] = new Vector3(x, height * 10f, y);
            uvs[y * Size + x] = new Vector2((float)x / Size, (float)y / Size);
        }

        int triIndex = 0;
        for (int y = 0; y < Size - 1; y++)
        for (int x = 0; x < Size - 1; x++)
        {
            int i = y * Size + x;
            tris[triIndex++] = i;
            tris[triIndex++] = i + Size;
            tris[triIndex++] = i + 1;

            tris[triIndex++] = i + 1;
            tris[triIndex++] = i + Size;
            tris[triIndex++] = i + Size + 1;
        }

        mesh.vertices = verts;
        mesh.triangles = tris;
        mesh.uv = uvs;
        mesh.RecalculateNormals();

        GetComponent<MeshFilter>().mesh = mesh;
    }
}
                

Drag the component onto an empty GameObject and hit Play to see the terrain generated.

Save/Load System (C#)

Provides asynchronous JSON serialization for game state using System.Text.Json and FileStream.


using System.IO;
using System.Text.Json;
using System.Threading.Tasks;

public class GameState
{
    public int Level { get; set; }
    public float PlayerHealth { get; set; }
    public Vector3 PlayerPosition { get; set; }
    // Add more fields as needed
}

public static class SaveLoad
{
    private static readonly string Path = System.Environment.GetFolderPath(
        System.Environment.SpecialFolder.ApplicationData) + "/MyGame/state.json";

    public static async Task SaveAsync(GameState state)
    {
        var json = JsonSerializer.Serialize(state, new JsonSerializerOptions { WriteIndented = true });
        await File.WriteAllTextAsync(Path, json);
    }

    public static async Task<GameState?> LoadAsync()
    {
        if (!File.Exists(Path)) return null;
        var json = await File.ReadAllTextAsync(Path);
        return JsonSerializer.Deserialize<GameState>(json);
    }
}
                

Call await SaveLoad.SaveAsync(currentState) before exiting and load on startup with await SaveLoad.LoadAsync().