Author: G455SuPXBUucr6XuX

  • Your First Unity 6 2D Game in Seven Testable Steps

    Your First Unity 6 2D Game in Seven Testable Steps

    Reviewed: 29 August 2026 · Next review: 28 February 2027
    Author: Ozlin Info Editorial Team · Human review: Lin

    A useful first game is small enough to finish and structured enough to test. This guide builds one scene in which a player moves a square to a goal, shows a success message and can be built for a selected target. It uses a pinned Unity 6 editor, the current Input System and Rigidbody2D.linearVelocity rather than the legacy Input.GetAxis workflow.

    The example is educational. Run it in a new project or branch, record exact versions and inspect every imported asset and package before using it in production.

    1. Pin the project and define “done”

    Install a supported Unity 6 LTS editor through Unity Hub with the module for the first target platform. Record the full editor version. Create a 2D project and initialise source control before adding assets.

    Commit Assets, Packages and ProjectSettings. Exclude generated Library, Temp, Logs, obj and local build directories using an appropriate Unity ignore file. Do not commit signing keys, service credentials or generated store packages.

    Write a tiny acceptance test:

    • the game opens directly into one scene;
    • WASD, arrow keys or a gamepad stick moves the player;
    • the player cannot pass through the border;
    • reaching the goal displays a success panel;
    • Escape or a visible control can exit or return according to platform; and
    • a built player runs on the selected target device.

    This boundary prevents a first project from expanding into inventory, networking and procedural worlds before its basic loop works.

    2. Build the scene with licensed placeholders

    Create and save Assets/Scenes/Main.unity. Add a camera with an orthographic projection. Use simple coloured sprites created in the editor or original files for the player, border and goal. Record the source and licence of anything downloaded; “free” does not define reuse or redistribution rights.

    Create sorting layers for background, world and interface if needed. Use consistent world units. Add four static border objects with BoxCollider2D, and give the player:

    • SpriteRenderer;
    • Rigidbody2D with gravity scale set to zero for this top-down example; and
    • BoxCollider2D or another shape matching the visual body.

    Do not resize a collider accidentally through a deeply scaled parent. Turn on collision gizmos and check the actual shape. Place the goal with a BoxCollider2D marked as a trigger.

    3. Create action-based input

    Unity documents the Input System as the extensible alternative recommended for new projects, while the old UnityEngine.Input API is legacy (Unity — Input System, Unity — legacy Input API).

    Install or confirm the released Input System package compatible with the pinned editor. Create Assets/Input/GameInput.inputactions with an action map named Player and a Move action:

    • action type: Value;
    • control type: Vector2;
    • a 2D Vector composite for WASD;
    • a second 2D Vector composite for arrow keys; and
    • a gamepad left-stick binding.

    Save the asset. The package can bind multiple devices to one action and supports later rebinding through overrides (Unity — Input bindings). Input handling belongs to gameplay actions rather than keyboard-specific code.

    4. Move the player through Rigidbody2D

    Create Assets/Scripts/TopDownMover.cs:

    using UnityEngine;
    using UnityEngine.InputSystem;
    
    [RequireComponent(typeof(Rigidbody2D))]
    public sealed class TopDownMover : MonoBehaviour
    {
        [SerializeField] private InputActionReference moveAction;
        [SerializeField, Min(0f)] private float speed = 5f;
    
        private Rigidbody2D body;
    
        private void Awake()
        {
            body = GetComponent<Rigidbody2D>();
        }
    
        private void OnEnable()
        {
            moveAction.action.Enable();
        }
    
        private void OnDisable()
        {
            moveAction.action.Disable();
            if (body != null)
            {
                body.linearVelocity = Vector2.zero;
            }
        }
    
        private void FixedUpdate()
        {
            Vector2 input = moveAction.action.ReadValue<Vector2>();
            if (input.sqrMagnitude > 1f)
            {
                input.Normalize();
            }
    
            body.linearVelocity = input * speed;
        }
    }

    Attach it to the player and assign the Move action reference. Unity 6's current Rigidbody2D API exposes linearVelocity as the linear velocity vector (Unity — Rigidbody2D.linearVelocity). Pinning the editor matters because older tutorials and versions use different API names.

    The script reads intent and applies velocity during the physics step. Normalising values above magnitude one prevents diagonal keyboard input from exceeding the configured speed. A platformer would need gravity, grounded checks, jump rules and a different controller; do not reuse this top-down movement unchanged.

    5. Add one goal and explicit game state

    Create a GoalZone component that raises one success event the first time the player enters. Keep outcome logic outside the movement script. A small GameFlow component can own Playing and Completed states, disable player input on completion and open a success panel.

    Validate the collider belongs to the player using a component or layer, not an object name. Guard repeated trigger callbacks so the score or success transition is idempotent. If the scene reloads, the new flow owner should start from a deliberate state.

    Add a reset control through an input action or accessible interface button. Do not require a mouse if gamepad is supported. Avoid relying only on colour or sound to communicate success.

    6. Add readable interface, audio and tests

    Create a Canvas with brief controls and a hidden success panel. Anchor elements so they survive several aspect ratios and safe areas. Use readable contrast and a logical navigation order. If a sound confirms success, keep the visible message as an alternative.

    Test the acceptance list in Play Mode, then add automated checks where useful:

    • an Edit Mode test for movement-vector normalisation;
    • a Play Mode test that the border blocks the body;
    • a Play Mode test that entering the goal completes once; and
    • a content check that the build scene is present.

    Try keyboard and a physical gamepad, unplug the gamepad, resize the window, lose and regain focus, and reload the scene. Watch the Console for exceptions and warnings. A tutorial that “looks right” but emits an error every frame is not complete.

    7. Create a Build Profile and run the player

    Open File → Build Profiles. Unity 6 Build Profiles let a project store multiple configurations and their scene lists as assets (Unity — Build Profiles). Create a development profile for the first target, add Main.unity, select the correct platform module and build to a clean local output directory.

    Run the built player and repeat the acceptance test. The editor does not reproduce every resolution, file path, input, graphics or lifecycle behaviour. For mobile, install on a physical device and check touch design, safe areas, suspend/resume, performance and package identity. Store submission additionally requires current signing, SDK, privacy and listing work; a local build is not store approval.

    Record the build date, source revision, editor and package versions and test device. Make a source-control tag when the seven-step sample passes. You now have a finished, reproducible base that can accept one new feature at a time.

    For a scoped software build or review, see Ozlin Info's secure web and software delivery service; related first-party game-engineering context is in the Projects archive, or contact Ozlin Info.

    Related reading: Cross-platform Unity architecture and delivery.


    General-information disclaimer

    This article provides an educational example, not a supported Unity package, storefront approval or production controller. Verify the pinned editor, packages, licences, platform requirements and tests for the actual project.

    AI-assistance disclosure

    AI tools assisted with source discovery, example drafting and copyediting. A human reviewer must create the project, compile the script, run the tests and verify current Unity and target-platform behaviour before publication or use.

    Primary sources checked

    Source access date: 29 August 2026.

  • Choose a 2D Physics Engine with a Reproducible Project Benchmark

    Choose a 2D Physics Engine with a Reproducible Project Benchmark

    Reviewed: 29 August 2026 · Next review: 28 February 2027
    Author: Ozlin Info Editorial Team · Human review: Lin

    A 2D physics engine is a dependency with consequences for gameplay feel, determinism, tooling, platform support and maintenance. Feature lists and synthetic “objects per second” results do not identify the best option for a particular game. Build a small benchmark from the real project's shapes, joints, queries and target devices, then record the decision.

    This review considers three categories as of 29 August 2026: Box2D, Rapier 2D and the physics system integrated into the chosen game engine. It does not declare a performance winner because no common project benchmark has been run.

    Establish non-negotiable requirements

    Before installing candidates, define:

    • supported languages, engines and platforms;
    • required shapes, joints, sensors, casts and continuous collision;
    • world scale, maximum speed and expected active-body range;
    • fixed-step and rollback or replay requirements;
    • editor, debugging and asset-authoring workflow;
    • multithreading and WebAssembly needs;
    • acceptable binary, memory and build-system impact;
    • licence, notices and source-distribution obligations; and
    • who will maintain bindings and upgrades.

    Separate must-have behaviour from convenient tooling. A game with deterministic rollback, deformable terrain or thousands of sleeping bodies has different priorities from a small puzzle game already built in an editor.

    Compare current options without flattening them

    Option Current characteristics to verify Licence and integration questions
    Box2D Portable C17 library; rigid bodies, convex shapes, sensors, joints, ray and shape casts, continuous collision, multithreading and SIMD are documented on current main Upstream is MIT licensed; verify the exact release, notices, build flags and whether a third-party language binding has a different lifecycle or licence
    Rapier 2D Rust engine with 2D and 3D crates, collision queries, CCD and optional parallel or SIMD features; official JavaScript bindings also exist Upstream repository is Apache-2.0; verify crate features, bindings, notices and target support for the pinned release
    Engine-integrated physics Editor components, scene serialisation, engine lifecycle, profiler and platform build integration can reduce custom glue Covered by the engine's terms and release lifecycle; verify exposed features, upgrade path, source access, platform restrictions and whether lower-level controls are available

    Box2D's current repository describes a C17 data-oriented engine under the MIT licence, with continuous collision, convex shapes, sensors, casts, joints, multithreading and SIMD (Box2D — repository, Box2D — licence). These statements concern upstream main at the review date; a packaged engine integration may use a different version or patch set.

    Rapier provides Rust crates for 2D and 3D and publishes official guides on collision, CCD and determinism. Its upstream repository uses Apache License 2.0 (Rapier — repository, Rapier — licence). Check which feature flags are enabled. Rapier's determinism guide explains that enhanced cross-platform determinism trades away SIMD and parallel features, and parallel execution can be slower for small scenes (Rapier — Determinism).

    An integrated option can be the lowest-risk choice when it already meets requirements. Avoid replacing it solely because another library wins an unrelated benchmark. Conversely, editor convenience does not resolve a missing collision feature or rollback constraint.

    Pin every test variable

    Create one comparison repository and record:

    • commit or package version and dependency lock;
    • compiler, flags, target architecture and enabled features;
    • operating system, hardware, power and thermal conditions;
    • fixed step, substeps, solver iterations and sleep settings;
    • length, warm-up and repetition count;
    • scene seed and initial state;
    • measurement method and raw output; and
    • known differences that prevent exact equivalence.

    Do not compare one debug build with another release build. Do not use default solver settings without recording them. Render the same simple debug view separately from the timed simulation so graphics do not distort results.

    Build benchmark scenes from the game

    Reviewed: 29 August 2026 · Next review: 28 February 2027
    Author: Ozlin Info Editorial Team · Human review: Lin

    A 2D physics engine is a dependency with consequences for gameplay feel, determinism, tooling, platform support and maintenance. Feature lists and synthetic “objects per second” results do not identify the best option for a particular game. Build a small benchmark from the real project's shapes, joints, queries and target devices, then record the decision.

    This review considers three categories as of 29 August 2026: Box2D, Rapier 2D and the physics system integrated into the chosen game engine. It does not declare a performance winner because no common project benchmark has been run.

    Establish non-negotiable requirements

    Before installing candidates, define:

    • supported languages, engines and platforms;
    • required shapes, joints, sensors, casts and continuous collision;
    • world scale, maximum speed and expected active-body range;
    • fixed-step and rollback or replay requirements;
    • editor, debugging and asset-authoring workflow;
    • multithreading and WebAssembly needs;
    • acceptable binary, memory and build-system impact;
    • licence, notices and source-distribution obligations; and
    • who will maintain bindings and upgrades.

    Separate must-have behaviour from convenient tooling. A game with deterministic rollback, deformable terrain or thousands of sleeping bodies has different priorities from a small puzzle game already built in an editor.

    Compare current options without flattening them

    Option Current characteristics to verify Licence and integration questions
    Box2D Portable C17 library; rigid bodies, convex shapes, sensors, joints, ray and shape casts, continuous collision, multithreading and SIMD are documented on current main Upstream is MIT licensed; verify the exact release, notices, build flags and whether a third-party language binding has a different lifecycle or licence
    Rapier 2D Rust engine with 2D and 3D crates, collision queries, CCD and optional parallel or SIMD features; official JavaScript bindings also exist Upstream repository is Apache-2.0; verify crate features, bindings, notices and target support for the pinned release
    Engine-integrated physics Editor components, scene serialisation, engine lifecycle, profiler and platform build integration can reduce custom glue Covered by the engine's terms and release lifecycle; verify exposed features, upgrade path, source access, platform restrictions and whether lower-level controls are available

    Box2D's current repository describes a C17 data-oriented engine under the MIT licence, with continuous collision, convex shapes, sensors, casts, joints, multithreading and SIMD (Box2D — repository, Box2D — licence). These statements concern upstream main at the review date; a packaged engine integration may use a different version or patch set.

    Rapier provides Rust crates for 2D and 3D and publishes official guides on collision, CCD and determinism. Its upstream repository uses Apache License 2.0 (Rapier — repository, Rapier — licence). Check which feature flags are enabled. Rapier's determinism guide explains that enhanced cross-platform determinism trades away SIMD and parallel features, and parallel execution can be slower for small scenes (Rapier — Determinism).

    An integrated option can be the lowest-risk choice when it already meets requirements. Avoid replacing it solely because another library wins an unrelated benchmark. Conversely, editor convenience does not resolve a missing collision feature or rollback constraint.

    Pin every test variable

    Create one comparison repository and record:

    • commit or package version and dependency lock;
    • compiler, flags, target architecture and enabled features;
    • operating system, hardware, power and thermal conditions;
    • fixed step, substeps, solver iterations and sleep settings;
    • length, warm-up and repetition count;
    • scene seed and initial state;
    • measurement method and raw output; and
    • known differences that prevent exact equivalence.

    Do not compare one debug build with another release build. Do not use default solver settings without recording them. Render the same simple debug view separately from the timed simulation so graphics do not distort results.

    Build benchmark scenes from the game

    Use several fixtures rather than one maximum-body stack:

    1. idle world: representative static geometry and sleeping bodies;
    2. active stack: contacts and joints under sustained motion;
    3. fast projectiles: thin targets and the required CCD policy;
    4. query load: ray, shape and overlap queries matching gameplay;
    5. creation burst: spawn, remove and reuse at a credible peak;
    6. large-world or streaming transition: only if the game needs it; and
    7. deterministic replay: identical input sequence and state checksums where required.

    Measure median and high-percentile step time, missed deadlines, memory high-water mark, allocation count, contact and broad-phase statistics, job utilisation and divergence. Averages can hide one-frame spikes that are visible to players.

    Run on the lowest supported device and one representative middle tier. Desktop results do not establish mobile or WebAssembly behaviour. Keep scene fixtures in source control so future upgrades can rerun them.

    Evaluate correctness and feel

    Performance is only one dimension. Review:

    • resting stability and jitter;
    • tunnelling and CCD edge cases;
    • joint limits, motors and break policy;
    • collision filtering and sensor events;
    • contact ordering and callback restrictions;
    • character-controller needs;
    • debugging and visualisation;
    • save, rollback and network integration; and
    • quality of diagnostics when input is invalid.

    Create golden event sequences and tolerance-based state comparisons. Bitwise equality may not be a supported promise. If cross-platform deterministic replay is required, test the exact targets and configuration rather than inferring it from a project description.

    Gameplay feel also needs human evaluation. The solver, step, units, damping, restitution and controller layer interact. A stable benchmark can still feel wrong for a platformer. Prototype one representative mechanic in each viable candidate.

    Make the dependency decision auditable

    Write an architecture decision record containing requirements, candidates, versions, licences, raw benchmark links, excluded options, trade-offs, upgrade owner and exit plan. Include a software-bill-of-materials entry and required notices. Review transitive bindings rather than assuming the upstream licence covers all integration code.

    Set an upgrade trigger: security issue, platform incompatibility, required feature, maintenance end or measured regression. Re-run the benchmark before a material upgrade and keep a rollback build.

    For a scoped software build or review, see Ozlin Info's secure web and software delivery service; related first-party game-engineering context is in the Projects archive, or contact Ozlin Info.

    Related reading: Collision detection from broad phase to CCD.


    General-information disclaimer

    This article provides general technical information and a comparison method, not benchmark results, legal advice or a product endorsement. Verify current releases, licences, platform terms and project behaviour before selection.

    AI-assistance disclosure

    AI tools assisted with source discovery, comparison structure and copyediting. A human reviewer must inspect licences, run the pinned benchmark, validate gameplay and approve the architecture decision before publication or adoption.

    Primary sources checked

    Source access date: 29 August 2026.

    Use several fixtures rather than one maximum-body stack:

    1. idle world: representative static geometry and sleeping bodies;
    2. active stack: contacts and joints under sustained motion;
    3. fast projectiles: thin targets and the required CCD policy;
    4. query load: ray, shape and overlap queries matching gameplay;
    5. creation burst: spawn, remove and reuse at a credible peak;
    6. large-world or streaming transition: only if the game needs it; and
    7. deterministic replay: identical input sequence and state checksums where required.

    Measure median and high-percentile step time, missed deadlines, memory high-water mark, allocation count, contact and broad-phase statistics, job utilisation and divergence. Averages can hide one-frame spikes that are visible to players.

    Run on the lowest supported device and one representative middle tier. Desktop results do not establish mobile or WebAssembly behaviour. Keep scene fixtures in source control so future upgrades can rerun them.

    Evaluate correctness and feel

    Performance is only one dimension. Review:

    • resting stability and jitter;
    • tunnelling and CCD edge cases;
    • joint limits, motors and break policy;
    • collision filtering and sensor events;
    • contact ordering and callback restrictions;
    • character-controller needs;
    • debugging and visualisation;
    • save, rollback and network integration; and
    • quality of diagnostics when input is invalid.

    Create golden event sequences and tolerance-based state comparisons. Bitwise equality may not be a supported promise. If cross-platform deterministic replay is required, test the exact targets and configuration rather than inferring it from a project description.

    Gameplay feel also needs human evaluation. The solver, step, units, damping, restitution and controller layer interact. A stable benchmark can still feel wrong for a platformer. Prototype one representative mechanic in each viable candidate.

    Make the dependency decision auditable

    Write an architecture decision record containing requirements, candidates, versions, licences, raw benchmark links, excluded options, trade-offs, upgrade owner and exit plan. Include a software-bill-of-materials entry and required notices. Review transitive bindings rather than assuming the upstream licence covers all integration code.

    Set an upgrade trigger: security issue, platform incompatibility, required feature, maintenance end or measured regression. Re-run the benchmark before a material upgrade and keep a rollback build.

    For a scoped software build or review, see Ozlin Info's secure web and software delivery service; related first-party game-engineering context is in the Projects archive, or contact Ozlin Info.

    Related reading: Collision detection from broad phase to CCD.


    General-information disclaimer

    This article provides general technical information and a comparison method, not benchmark results, legal advice or a product endorsement. Verify current releases, licences, platform terms and project behaviour before selection.

    AI-assistance disclosure

    AI tools assisted with source discovery, comparison structure and copyediting. A human reviewer must inspect licences, run the pinned benchmark, validate gameplay and approve the architecture decision before publication or adoption.

    Primary sources checked

    Source access date: 29 August 2026.

  • Build a Stable Pygame Loop with Fixed Updates and Interpolation

    Build a Stable Pygame Loop with Fixed Updates and Interpolation

    Reviewed: 29 August 2026 · Next review: 28 February 2027
    Author: Ozlin Info Editorial Team · Human review: Lin

    A game loop repeatedly processes operating-system events, advances game state and renders a frame. The order is simple; the timing details are not. A loop that multiplies movement by “one unit per frame” runs at different speeds on different machines, while an unbounded time step can make physics unstable after a pause or debugger stop.

    This example uses pygame-ce and separates variable rendering from a fixed simulation step. It is intentionally small, but it includes event pumping, frame-time clamping, focus handling, interpolation and clean shutdown.

    Understand what Clock.tick() returns

    pygame.time.Clock.tick(framerate) waits as needed to limit the loop and returns the elapsed milliseconds since the previous call. Divide by 1,000 to obtain seconds. The documentation notes that its timing uses the platform delay function and is not perfectly accurate; a frame cap is a pacing aid, not a real-time guarantee (pygame-ce — pygame.time).

    A variable-step update can be adequate for visual motion:

    position += velocity * frame_seconds

    Physics and collision often behave more consistently with a fixed step. The accumulator pattern collects elapsed frame time and runs zero or more updates of a constant duration. Rendering interpolates between the two most recent simulation states.

    Install and run the example

    Create a virtual environment, install the selected pygame-ce version and record it in the project dependency file:

    python -m venv .venv
    python -m pip install pygame-ce

    Save this as main.py:

    import pygame
    
    
    WINDOW_SIZE = (960, 540)
    FIXED_SECONDS = 1.0 / 120.0
    MAX_FRAME_SECONDS = 0.25
    RENDER_LIMIT = 144
    MOVE_SPEED = 260.0
    PLAYER_SIZE = pygame.Vector2(44.0, 44.0)
    
    
    def read_direction() -> pygame.Vector2:
        keys = pygame.key.get_pressed()
        direction = pygame.Vector2(
            float(keys[pygame.K_d]) - float(keys[pygame.K_a]),
            float(keys[pygame.K_s]) - float(keys[pygame.K_w]),
        )
        if direction.length_squared() > 1.0:
            direction = direction.normalize()
        return direction
    
    
    def clamp_to_window(position: pygame.Vector2) -> pygame.Vector2:
        return pygame.Vector2(
            max(0.0, min(position.x, WINDOW_SIZE[0] - PLAYER_SIZE.x)),
            max(0.0, min(position.y, WINDOW_SIZE[1] - PLAYER_SIZE.y)),
        )
    
    
    def main() -> None:
        pygame.init()
        screen = pygame.display.set_mode(WINDOW_SIZE)
        pygame.display.set_caption("Fixed-step pygame-ce loop")
        clock = pygame.time.Clock()
    
        position = pygame.Vector2(120.0, 240.0)
        previous_position = position.copy()
        accumulator = 0.0
        running = True
        focused = True
    
        while running:
            frame_seconds = min(
                clock.tick(RENDER_LIMIT) / 1000.0,
                MAX_FRAME_SECONDS,
            )
    
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                    running = False
                elif event.type == pygame.WINDOWFOCUSLOST:
                    focused = False
                    accumulator = 0.0
                elif event.type == pygame.WINDOWFOCUSGAINED:
                    focused = True
    
            if not running:
                break
    
            if focused:
                accumulator += frame_seconds
                direction = read_direction()
    
                while accumulator >= FIXED_SECONDS:
                    previous_position = position.copy()
                    position += direction * MOVE_SPEED * FIXED_SECONDS
                    position = clamp_to_window(position)
                    accumulator -= FIXED_SECONDS
            else:
                previous_position = position.copy()
    
            alpha = accumulator / FIXED_SECONDS
            render_position = previous_position.lerp(position, alpha)
    
            screen.fill("#10131a")
            player_rect = pygame.Rect(
                round(render_position.x),
                round(render_position.y),
                round(PLAYER_SIZE.x),
                round(PLAYER_SIZE.y),
            )
            pygame.draw.rect(screen, "#e63946", player_rect, border_radius=8)
            pygame.display.flip()
    
        pygame.quit()
    
    
    if __name__ == "__main__":
        main()

    Run it with python main.py. WASD moves the square and Escape exits.

    Why the loop is structured this way

    Events are pumped every outer frame

    pygame.event.get() keeps the window responsive and gives the application a chance to handle quit, focus and device events. Do not process events only inside the fixed-step loop: a fast render frame may execute no simulation step, while a delayed frame may execute several.

    The frame delta is clamped

    After a breakpoint, window drag or device stall, the reported delta can be very large. Advancing every missed fixed step can cause a “spiral of death” in which catch-up work makes the next frame even later. This example clamps one outer-frame contribution to 250 milliseconds and clears the accumulator on focus loss. A networked or deterministic game needs a more explicit pause and resynchronisation policy.

    Simulation uses a constant delta

    Movement advances in 1/120-second increments. The render cap and simulation rate are separate: changing RENDER_LIMIT does not change simulation speed. A production project should choose a step supported by its collision and CPU budget; 120 Hz is an example, not a universal recommendation.

    If each update takes longer than the fixed interval, the loop cannot catch up. Add a measured maximum number of steps per frame and telemetry rather than silently dropping time. Decide whether the game should slow, skip presentation or resynchronise.

    Rendering interpolates

    The accumulator contains the fraction of time between the previous and current simulation state. lerp presents a position between them, which can make rendering smoother when it runs more frequently than simulation. This adds approximately one simulation step of visual latency and should interpolate presentation only; do not feed the rendered position back into gameplay.

    The example samples held keyboard state once per outer frame. For very precise input, queue timestamped transitions and consume them at defined simulation boundaries. A multiplayer game must align inputs with its networking tick and authority model.

    Extend it without losing testability

    Move simulation into a function or model that accepts commands and a fixed delta without reading pygame globals. Unit tests can then advance a known number of steps and compare state. Keep rendering read-only and keep random number generation behind a seeded interface.

    Add systems in a deliberate order:

    1. action-based input rather than hard-coded keys;
    2. a small world model and collision tests;
    3. asset loading outside the hot loop;
    4. scene or state ownership;
    5. audio and presentation requests; and
    6. profiler counters for update, render and allocations.

    Test window resize, focus loss, long runtime, device disconnect and a deliberately slow update. Package a built application on the target operating system; an editor or shell run is not the full release environment.

    For a scoped software build or review, see Ozlin Info's secure web and software delivery service; related first-party game-engineering context is in the Projects archive, or contact Ozlin Info.

    Related reading: Collision detection from broad phase to CCD.


    General-information disclaimer

    This article provides an educational example, not a supported game framework or timing guarantee. Review dependencies, licences, platform packaging, input, accessibility and performance for the actual project.

    AI-assistance disclosure

    AI tools assisted with source discovery, code drafting and copyediting. A human reviewer must run the example, pin dependencies, add tests and verify current pygame-ce and Python behaviour before publication or use.

    Primary sources checked

    Source access date: 29 August 2026.

  • Docker Compose in Production: A Defensible Single-Host Pattern

    Docker Compose in Production: A Defensible Single-Host Pattern

    Reviewed: 29 August 2026 · Next review: 28 February 2027
    Author: Ozlin Info Editorial Team · Human review: Lin

    Docker Compose can be a practical production option for a modest application on one well-managed server. It provides a declarative description of services, networks, volumes and runtime configuration, and Docker documents a production workflow using a base Compose file with a production-specific override (Docker Docs — Use Compose in production).

    That does not make one Compose host highly available. The host, storage and Docker daemon can remain single points of failure. If the service requires automatic rescheduling across machines, multi-node availability or sophisticated traffic management, evaluate an orchestrator or managed platform instead of implying that a restart policy solves infrastructure failure.

    Define the operating target first

    Record the service-level needs before writing YAML:

    • expected traffic and resource profile;
    • tolerable downtime and data loss;
    • backup and restore objectives;
    • public and private network paths;
    • data classification and secrets;
    • patch, release and rollback ownership;
    • monitoring and alert response; and
    • conditions that require migration beyond one host.

    For a small content site, several minutes of controlled recovery may be acceptable. A payment or safety-critical service may need a very different architecture. Compose is a packaging and lifecycle tool, not a substitute for risk assessment.

    Build immutable, reviewable images

    Production application code should normally be inside a versioned image rather than bind-mounted from a mutable source directory. Use a multi-stage Dockerfile so compilers, package managers and build-time credentials stay out of the final runtime image. Choose a small, trusted base image, install only required packages and run as a non-root user where the application permits it.

    Docker notes that tags are mutable. Pinning an image digest improves reproducibility, but it also means the team must deliberately update the digest to receive fixes. Automate notifications or pull requests rather than pinning and forgetting (Docker Docs — Building best practices).

    A defensible release records:

    • source commit and build workflow;
    • image repository, tag and digest;
    • software bill of materials or dependency inventory where appropriate;
    • vulnerability-review result and accepted exceptions;
    • configuration version and database migration; and
    • deployment and rollback evidence.

    Do not bake passwords, API keys or private certificates into an image layer. Removing a secret in a later layer does not reliably erase it from earlier image history.

    Separate development and production configuration

    Keep common service definitions in compose.yaml and apply production differences through a reviewed override such as compose.production.yaml:

    services:
      web:
        image: registry.example.test/acme-web@sha256:REPLACE_WITH_APPROVED_DIGEST
        restart: unless-stopped
        read_only: true
        tmpfs:
          - /tmp
        ports:
          - "127.0.0.1:8080:8080"
        healthcheck:
          test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/healthz"]
          interval: 30s
          timeout: 5s
          retries: 3
          start_period: 20s
        security_opt:
          - no-new-privileges:true
        networks:
          - edge
          - app
    
      database:
        image: mysql@sha256:REPLACE_WITH_APPROVED_DIGEST
        restart: unless-stopped
        volumes:
          - db-data:/var/lib/mysql
        networks:
          - app
    
    networks:
      edge: {}
      app:
        internal: true
    
    volumes:
      db-data: {}

    This is an illustration, not a drop-in deployment. The image may not support a read-only filesystem, wget, the shown port or non-root operation. Validate the actual image and application.

    Render the combined configuration before deployment:

    docker compose -f compose.yaml -f compose.production.yaml config

    Review the output for unintended port exposure, missing variables, duplicate mounts and development flags.

    Expose the minimum network surface

    Publish only ports that genuinely need host access. If a reverse proxy on the same host is the only caller, bind the application to loopback rather than every interface. Keep databases and queues on internal networks without published host ports unless an authorised operational requirement says otherwise.

    Network separation reduces accidental reachability but is not an authorisation system. The application must still authenticate callers, authorise actions, validate input and protect transport where traffic crosses an untrusted boundary.

    Avoid privileged containers, host network mode, broad Linux capabilities and Docker socket mounts unless a reviewed use case requires them. Access to the Docker socket is effectively high-impact control of the host.

    Handle secrets according to the deployment model

    Compose can mount declared secrets into a service as files, which is generally preferable to copying them into the image or printing them in environment dumps. On standalone Compose, however, the source secret is still a host file or external resource that the operator must protect. Docker Swarm secrets add encrypted transport and at-rest handling for Swarm services; those guarantees do not automatically apply to every standalone Compose deployment (Docker Docs — Manage sensitive data with Docker secrets).

    Use restrictive host permissions or an appropriate secret manager, grant each service only the secrets it needs, avoid logging values and define rotation. Treat .env as configuration convenience, not an encrypted vault, and keep secret-bearing files out of source control and build context.

    Use health checks without confusing them with monitoring

    A health check should test whether the service can perform a small, representative local function. Compose can wait for a dependency marked service_healthy before creating a dependent service (Docker Docs — Control startup order). This helps startup sequencing, but it does not guarantee that a remote user can reach the application or that every dependency works.

    Combine container health with external availability checks, application metrics, structured logs and host monitoring. Track disk, memory, CPU, file descriptors, container restarts, certificate expiry, backup results and application-specific failures. Put retention and access controls around logs because they may contain personal or security-relevant information.

    Set memory and CPU expectations carefully. A hard limit can contain one service but can also cause abrupt failure under legitimate load. Observe real usage, preserve host capacity and test behaviour when a limit is reached.

    Separate persistent data from disposable containers

    Containers should be replaceable. Store mutable application data in named volumes, bind mounts with explicit ownership or external services. Document exactly what must be backed up: database-consistent data, uploads, configuration, certificates, encryption keys and any queue or object storage required for recovery.

    A copy of a live database directory is not automatically a valid backup. Use the database's supported logical or physical backup method, protect the result and test restoration into an isolated environment. Record recovery time and recovered data point rather than merely checking that a backup file exists.

    Named volumes do not create backups, replication or geographic resilience. They only separate data lifecycle from a particular container.

    Release one controlled change at a time

    A single-host deployment can use this sequence:

    1. build and test the image in CI;
    2. approve the image digest and configuration;
    3. take or verify the required backup;
    4. pull images without replacing running containers;
    5. run backward-compatible database migrations where possible;
    6. recreate the affected service;
    7. verify health, external behaviour, logs and data; and
    8. retain a tested rollback route.

    Docker's production guide shows rebuilding and recreating one service with docker compose build web followed by docker compose up --no-deps -d web. When deploying registry images, use the equivalent controlled pull and up flow for the exact approved reference. Understand that recreating a container can cause brief downtime on one host.

    Rollback must account for data schema. Restoring an earlier application image may fail after an irreversible migration. Prefer expand-and-contract migrations, backups and explicit compatibility windows.

    Patch the complete stack

    Rebuild images regularly with updated base images and application dependencies, then test and deploy them. Also patch the host kernel, Docker Engine, Compose plugin and reverse proxy. Schedule reboot paths and verify containers return as expected.

    Review configuration drift with docker compose config, image digests and host records. Do not use latest as an undocumented release decision, and do not enable unattended replacement of stateful services without tested compatibility and rollback.

    For help designing or reviewing a scoped web hosting deployment, see Ozlin Info's web development services or contact Ozlin Info.

    Related reading: Incident response and disaster recovery: design for evidence and recovery.


    General-information disclaimer

    This article provides general technical information only. It is not a complete architecture, security assessment, availability commitment or backup design. Production controls must reflect the actual application, images, host, data and recovery requirements.

    AI-assistance disclosure

    AI tools assisted with source discovery, outlining and copyediting. A human reviewer must validate every command, image capability, deployment assumption, service claim and publication decision before release. No uptime, security or recovery outcome is guaranteed.

    Primary sources checked

    Source access date: 29 August 2026.

  • Web Accessibility with WCAG 2.2: A Practical Delivery Guide

    Web Accessibility with WCAG 2.2: A Practical Delivery Guide

    Reviewed: 29 August 2026 · Next review: 28 February 2027
    Author: Ozlin Info Editorial Team · Human review: Lin

    Web accessibility is the practice of making digital content and functionality usable by people with a wide range of disabilities, technologies and situations. It is not a final audit checkbox. Decisions made in research, content, visual design, component design, development, procurement and maintenance all affect whether people can complete a task.

    The current W3C Recommendation is Web Content Accessibility Guidelines (WCAG) 2.2. WCAG is organised around four principles: content must be perceivable, operable, understandable and robust. A Level AA conformance claim requires every applicable Level A and Level AA success criterion to be satisfied for the full pages in scope—not an average score across selected checks (W3C — WCAG 2.2).

    WCAG is a technical standard, not a complete description of every person's experience and not, by itself, a legal opinion. An organisation should separately determine which laws, procurement rules, contracts or policies apply to its website.

    Define scope and target before changing components

    Begin with the service people need to use. Record:

    • the pages, templates, states, documents and third-party journeys in scope;
    • target users, assistive technologies and supported browsers;
    • the intended WCAG version and conformance level;
    • critical tasks such as finding information, purchasing, registering or contacting support;
    • who owns design, code, content and external widgets; and
    • how defects will be prioritised, accepted and retested.

    For a new or substantially redesigned business site, WCAG 2.2 Level AA is a sensible engineering target. It includes the earlier WCAG 2.1 criteria and adds requirements such as focus not being obscured, alternatives to dragging, minimum target size, consistent help, avoiding unnecessary repeated entry and accessible authentication. WCAG 2.2 removed the obsolete 4.1.1 Parsing criterion, although a contract or policy that explicitly names an earlier WCAG version may still require separate reporting (W3C — What's New in WCAG 2.2).

    Do not publish “WCAG compliant” based only on a home-page scanner. A formal claim has specific scope and documentation requirements, and third-party content can affect the outcome.

    Prefer native, semantic HTML

    Native elements carry established keyboard and accessibility behaviour. Use a real <button> for an action, an <a href> for navigation, labelled form controls, ordered heading levels and landmarks such as header, nav, main and footer. Add ARIA only where native HTML cannot express the required name, role, state or relationship.

    A styled div with a click handler does not automatically gain button semantics, keyboard activation, focus behaviour or disabled state. Recreating these features increases code and test burden. If a custom widget is necessary, follow the appropriate WAI-ARIA Authoring Practices pattern and test the implemented behaviour; adding a role alone does not make it accessible.

    Content also needs structure and meaning:

    • give each page a descriptive title and one clear primary heading;
    • write link text that makes sense in context;
    • provide useful alternative text for informative images and empty alt text for decorative images;
    • provide captions for prerecorded video and an appropriate transcript for audio information;
    • identify the page language and language changes; and
    • present instructions and errors in text, not colour or position alone.

    Alternative text should communicate the image's purpose in that context. It is not a keyword field and does not need to describe every visible detail.

    Design for more than one way to interact

    Every interactive task should work without a mouse. Test forward and reverse keyboard navigation, logical focus order, visible focus, modal entry and exit, menus, disclosures, validation and any custom control. Focus must not be trapped or hidden behind sticky headers, cookie banners or other author-created content.

    Colour contrast matters, but colour is only one part of perceivability. Check text, controls, focus indicators and meaningful graphical objects against the applicable criterion. Do not use colour alone to communicate an error, status or selection.

    Responsive layouts must remain usable when people enlarge text or zoom. Check narrow viewports and 400% zoom for lost content, overlapping controls and two-dimensional scrolling where the criterion does not permit it. Fixed-height cards and clipped navigation often fail before the colour palette does.

    Authentication deserves particular attention. WCAG 2.2's Accessible Authentication criterion limits cognitive function tests such as memorising or transcribing information unless an alternative or assistance is available. Support password managers and paste; do not block them in the name of security without a carefully assessed reason.

    Make forms understandable and recoverable

    Each control needs a programmatically associated, visible label. Group related radio buttons or checkboxes with fieldset and legend when appropriate. Explain required formats before they are needed and identify required fields without relying on colour alone.

    When validation fails:

    1. retain safe values the user already entered;
    2. provide a clear summary and field-specific message;
    3. associate the error with its field;
    4. move or manage focus deliberately so the error is discoverable; and
    5. tell the user how to correct it.

    For an asynchronous submission, expose the result as a programmatically determinable status message. Do not unexpectedly move focus for every small update.

    Combine tools with human evaluation

    Automated tools are useful for repeatable checks such as missing accessible names, some contrast failures and certain invalid relationships. They cannot reliably judge whether alternative text is meaningful, focus order follows the task, instructions make sense or a screen-reader experience is coherent. W3C explicitly says no tool alone can determine whether a site meets accessibility guidelines (W3C — Introduction to Web Accessibility).

    A practical test set includes:

    Method What it can reveal
    Automated rules in CI Repeatable detectable regressions across known templates
    Keyboard-only walkthrough Reachability, order, traps, focus visibility and operability
    Zoom and reflow checks Clipping, overlap, loss of content and excessive scrolling
    Screen-reader checks Names, roles, headings, landmarks, reading order, status and errors
    High-contrast or forced-colour checks Information lost when authored colours are overridden
    Content review Heading logic, link purpose, instructions, captions and alternatives
    Disabled-user evaluation Barriers, workarounds and priorities that technical inspection can miss

    Test representative pages and every distinct component or state, not just URLs selected at random. Include errors, empty results, loading, authentication, session expiry and third-party flows. W3C's Easy Checks are a useful first review but are explicitly not exhaustive (W3C — Easy Checks).

    Keep an evidence-based remediation backlog

    Record each finding with the affected task, URL or component, WCAG criterion, reproducible steps, observed and expected behaviour, severity, owner, target release and retest evidence. Prioritise barriers that block critical tasks, affect many pages or create safety, privacy or financial consequences.

    Reusable components create leverage: correcting a shared navigation, dialog, form field or error summary can remove the same barrier across many pages. Add a regression test where automation is reliable, but retain manual checks in the definition of done.

    An accessibility statement should be accurate about scope, known limitations and contact paths. It should not claim perfection or replace a working way for people to report a barrier. Give accessibility reports an owner and response process.

    Treat accessibility as ongoing quality

    Content edits, plugin updates, third-party scripts and new features can reintroduce barriers. Review accessibility during discovery and design, test components before release, run automated rules in continuous integration and schedule periodic task-based evaluation. Train the people who publish content as well as the developers who build templates.

    For help reviewing a website workflow, component library or remediation backlog, see Ozlin Info's web development services or contact Ozlin Info.

    Related reading: 10 WordPress performance checks before optimisation.


    General-information disclaimer

    This article provides general technical information only. It is not legal, regulatory, procurement or accessibility-conformance advice. A conformance claim requires evaluation of the complete defined scope against the relevant standard and may require qualified legal or accessibility advice.

    AI-assistance disclosure

    AI tools assisted with source discovery, outlining and copyediting. A human reviewer must verify every technical statement, link, service claim and publication decision before release. Automated or AI-assisted checks do not prove accessibility or WCAG conformance.

    Primary sources checked

    Source access date: 29 August 2026.