今日已更新 88 条资讯 | 累计 40862 条内容
关于我们

标签:#game

找到 202 篇相关文章

AI 资讯

Presentation: A Solopreneur's Journey: From Engineer to Puzzle Master and Storyteller

Joe Cassavaugh shares his journey from software engineer to successful solopreneur with a $2M+ indie franchise. He explains how he scaled production to 10 games in 5 years, adopted Unity to boost velocity 4-6x, optimized content pipelines, and leveraged refactoring patterns. He discusses key trade-offs between corporate engineering and solopreneurship for senior devs and leaders. By Joe Cassavaugh

2026-09-08 原文 →
AI 资讯

Building Satisfying Shooting Mechanics in Unity: A Technical Breakdown Using a Piñata-Style Shooter

Shooting mechanics are deceptively simple to prototype and shockingly hard to make feel good . Any developer can spawn a projectile and check for collisions in an afternoon. But the difference between a shooting game that feels floaty and forgettable versus one that feels punchy, satisfying, and addictive comes down to a handful of technical decisions most tutorials skip entirely: hit detection precision, feedback timing, physics tuning, and performance discipline on low-end devices. In this article, I want to walk through the core systems that go into building a mobile shooting game — using a piñata-style target shooter as the working example, since this sub-genre is a great teaching tool. It combines projectile mechanics, physics-based destruction, particle feedback, and score systems into a compact, easy-to-reason-about package. Whether you're building this exact genre or a completely different shooter, the underlying systems are transferable. Why Target-Shooting Games Are a Great Case Study Before diving into code-level concerns, it's worth understanding why this genre specifically is such a useful learning framework for Unity developers. A piñata-shooting mechanic strips a shooter down to its purest form: aim, fire, hit, reward. There's no complex inventory system, no enemy AI pathfinding, no multiplayer netcode to worry about. That simplicity makes it the perfect sandbox for really nailing the fundamentals — projectile physics, collision precision, and juicy feedback — without getting distracted by unrelated systems. At the same time, it's not trivially simple. To make a target-shooter feel good, you still need to solve: Consistent, fair hit detection across different screen sizes and aspect ratios Physics-based destruction that looks satisfying without tanking frame rate Particle and reward feedback that reinforces every successful hit Difficulty scaling through target size, movement, and timing Performance optimization so the game runs smoothly even on budge

2026-09-08 原文 →
AI 资讯

BVH for Collision Detection: From AABB to Optimal Hierarchies

Table of Contents Why Broad-Phase Exists (and why naive O(N²) dies at 10k objects) Bounding Volume Hierarchy: The Data Structure That Scales Topology Choices: Binary vs. Multi-Branch, Pointer vs. Array Layout Construction Algorithms: From Naive to SAH-Optimal Traversal Strategies for Collision Queries The Static/Dynamic Dichotomy: Why One Tree Cannot Serve Two Masters The Dual-BVH Architecture Preview 1. Why Broad-Phase Exists The Pairwise Problem Every collision detection system faces the same fundamental challenge: given N objects, determine which pairs might be colliding so the expensive narrow-phase (SAT, GJK, EPA) only runs on plausible candidates. The naive approach tests every pair: // Naive O(N²) broad-phase — dies at ~10k objects std :: vector < CollisionPair > broadPhaseNaive ( const std :: vector < Object *>& objects ) { std :: vector < CollisionPair > pairs ; for ( size_t i = 0 ; i < objects . size (); ++ i ) { for ( size_t j = i + 1 ; j < objects . size (); ++ j ) { if ( aabbOverlap ( objects [ i ] -> aabb , objects [ j ] -> aabb )) { pairs . emplace_back ( objects [ i ], objects [ j ]); } } } return pairs ; } Complexity: O ( N ² ) AABB tests. At 60 Hz you have 16.67 ms/frame. At 120 Hz: 8.33 ms. Objects (N) Pairwise Tests @ 3 ns/test Frame Budget (60 Hz) 100 4,950 0.015 ms Trivial 1,000 499,500 1.5 ms Comfortable 10,000 49,995,000 150 ms 10x over budget 100,000 ~5x10^9 15,000 ms Impossible Cache Miss Catastrophe The pairwise loop doesn't just do too much work, it does it poorly . Each iteration accesses two random objects in memory. With 10k objects, you're thrashing L3 cache every frame. The BVH approach exploits spatial coherence: nearby objects in space are nearby in the tree, turning random access into sequential scans. The Real Job: Proving Separation KEY INSIGHT: Broad-phase is a rejection machine. Broad-phase is not about finding collisions. It's about proving separation as cheaply as possible. Every AABB overlap test that returns false is a vic

2026-09-07 原文 →
AI 资讯

Devlog: capturing smooth game footage from a renderer that never hits 30fps

Hey guys 👋 Quick devlog on the side project. I'm building an open-world stickman superhero game. Flat white surfaces, black outlines, no textures and no colour anywhere. The whole city is built from modules on a grid rather than baked meshes, which is the load-bearing decision of the project: destroying a wall is removing a module and building one is adding it back, so destruction and construction are the same system. This week went into the landscapes, so I wanted a 20 second clip flying through a few of the districts. The bit that was actually interesting I wanted the footage captured out of the real game rather than reconstructed in an editor. The obvious approach is to drive it with Playwright and take a screenshot every frame, but that falls apart immediately: rendering under automation is far slower than a screenshot loop can keep up with, so wall-clock capture stutters and the timing drifts. The fix is to stop letting the clock decide. Before the game boots, hijack requestAnimationFrame and queue the callbacks instead of running them: replace requestAnimationFrame with a function that pushes the callback onto a queue- expose a step(dt) that advances a virtual timestamp and drains the queue- call step(1000 / 30) once per screenshotEvery captured frame now advances the simulation by exactly 1/30th of a second, whatever the renderer is actually doing. A frame that takes 300ms to draw and a frame that takes 8ms produce identical motion. The result is smooth 30fps footage from a renderer that never once hit 30fps, and it is deterministic — the same seed gives you the same clip every time. The same rig drives the camera: for the aerials it detaches the chase camera and dollies an external one between two framings, and for the traversal and combat shots it just feeds synthetic input to the real player controller. Nothing in the video is staged. ## Stack Three.js driven imperatively, Rapier for physics, React for the HUD only, TypeScript in strict mode, packaged with

2026-09-07 原文 →
AI 资讯

I built a 16-bit RPG inside Jira, and Forge took away my server

I could not make myself log time in Jira. Not because it is hard. Because nothing happens afterwards. You type a number into a box, the box says nothing back, and by Thursday the habit is gone again. Every tool I tried fixed this by adding another box. So I built the missing half instead. Feed The Troll gives everyone on a team a pixel-art troll that gains XP from the work they already do in Jira, and turns sprint results into a village the whole project shares. It is on the Atlassian Marketplace now. This post skips the game itself. It is about five problems that turned out to be hard in ways I did not expect, each one a consequence of building the thing on Atlassian Forge, alone. What Forge gives you, and what it takes back Forge runs your code on Atlassian's infrastructure. There is no server of mine anywhere in the picture. That is the line on the listing page, and it was the single fact that shaped every decision underneath it. You get a Node 22 runtime, Forge SQL (TiDB under the hood) for storage, and Custom UI modules that reach the backend through @forge/bridge . You give up a backend you control, a cache you can reach, and outbound HTTP to anything you did not declare. The one that keeps mattering: any way to open the database at three in the morning and fix a single row by hand. The whole app declares six scopes. None of them are write scopes: read:board-scope:jira-software read:issue-details:jira read:jira-work read:jira-user read:sprint:jira-software storage:app That last line is the entire persistence layer. Twenty-one tables live behind it now, but only ten shipped with v1.0: trolls, XP events, daily activity, kudos, quests, inventory, team quests, villages, raids, project settings. Every table added since arrived the only way the platform makes comfortable, as a new migration appended to the list, never an edit to one already deployed. migrationRunner . enqueue ( ' v001_create_trolls ' , CREATE_TROLLS_TABLE ) // ... . enqueue ( ' v012_create_product_m

2026-09-07 原文 →
AI 资讯

Testing a deterministic browser game: seeds, replay and invalid state

A random game is easier to debug when the same inputs produce the same result. In HoopTrait, a browser basketball project, the Lab mode combines eight selected traits and generates a fictional career. The interesting engineering problem is keeping replay, sharing and validation consistent. This is a technical development note, not a claim that a game score predicts an athlete's real performance. Store the decisions, not just the result The Lab state records a seed, a dataset version and an ordered list of actions. An action is a pick or a reroll. Replaying those actions reconstructs the build. A seed alone is not a complete replay contract: changing the player pool or its order can change a seeded draw. A dataset version therefore matters alongside the random seed. For a future release, the same principle should apply to changes in the rules themselves. Test invariants across many runs The Lab test suite iterates through 1,000 seeds. For each seed it shuffles the order of the eight skills, uses the two allowed rerolls, and completes a build. It checks that: Eight distinct players were selected. All eight traits are present, and no player remains to be drawn after completion. The overall game score stays between 0 and 99 and matches the shared rating function. Packing and unpacking the share state returns the original state. Recomputing the fictional career returns the same output. The ten simulated seasons sum to the displayed career earnings. Those assertions catch different problems. A stable score does not prove that a shared link reproduces the same selections. A complete build does not prove that its season totals add up. Reject impossible histories A share payload is untrusted input, even in a client-side game. Negative or fractional seeds, duplicate skill picks, a third reroll, unknown action types, a mismatched dataset version and actions after completion are rejected. The tests also cover malformed encoded payloads and unexpected fields. Local state is usef

2026-09-06 原文 →
AI 资讯

No card ships until a blind judge passes it

My puzzle app, Keyhole, carries 296 dark stories, each with an illustrated card. A dark story is a situation that looks impossible until you drop one false assumption you did not know you were making, and the illustration must show the situation and never the reveal. Draw the aeroplane over the desert and story one is over before the player has read it. In August I ruled that the app does not ship while any card is still flagged by the judge. "End of story," I wrote in the decision, and then spent two days learning what that sentence cost. Two things get judged, the text and the art, and one design is shared by both. The judge is a model, run blind: it sees the finished card and the story the player sees, and neither the finding that triggered the redraw nor the old card. That is the whole trick. A judge that knows what was wrong last time grades the fix. A judge that knows nothing grades the card. Blindness is what makes a pass mean something, and it is why the judge is a separate call from the writer and from the illustrator, never the same conversation. The text pass first. A rubric written for the genre, with one test at its centre, "name the one assumption the solver will make that is false", and four semantic questions after it: does the reveal explain everything the situation promised, does the situation give the reveal away, is there a contradiction, can the answer be reached by yes/no questions without knowledge nobody has. Over all 296 stories it flagged 27: five unanswered, nine spoilers, ten sense breaks, three unsolvable. The fix lane rewrites only what a finding names, the deterministic gate must still pass, and the blind judge reads the result cold before it is written back. A fact-check over the rewrites then cleared them, or left a truth note where no honest fix existed. The art pass is where the numbers live. Each open card was redrawn from a scene brief and judged blind, in waves. The judge wrote a note on every failure, and the lever changed from

2026-09-06 原文 →
AI 资讯

Stop changing your sprite sheet to fix animation speed

An eight-frame animation does not have a fixed duration. At 8 fps it lasts one second; at 12 fps it lasts two-thirds of a second; at 16 fps it lasts half a second. Before drawing or generating more frames, check whether the problem is missing poses or the time each pose stays on screen. We maintain FrameSprite, a browser workspace for game assets. This is a timing and export note, not a claim that a particular frame count makes AI animation reliable. The equations work with hand-drawn sprites too. Three numbers that are easy to mix up Source FPS describes how a recording was sampled. Frame count is the number of entries you put in an animation. Playback FPS controls how fast those entries advance in the game. A 24 fps source video can provide eight selected poses that you play at 12 fps. You do not need to preserve every source frame. For equal holds, forward playback and a speed multiplier of 1: duration_seconds = frame_count / playback_fps frame_hold_ms = 1000 / playback_fps fps_for_target = frame_count * 1000 / target_duration_ms Same eight frames Hold per frame Full loop 8 fps 125 ms 1.000 s 12 fps 83.333… ms 0.667 s 16 fps 62.5 ms 0.500 s You changed the cadence without changing one pixel of the sprite sheet. A test you can reproduce Use the public eight-frame sample . Keep the same frames, order, canvas and pivot for all three trials. Change only playback FPS between 8, 12 and 16. Check the animation alone at its intended game size. Run it beside actual movement or attack timing. If cadence improves but a foot or weapon still jumps, inspect the missing phase instead of raising FPS again. If every frame jumps by a small amount, inspect canvas and pivot alignment. If the pause happens only at the seam, look for an accidental duplicate endpoint. The sample makes the arithmetic test repeatable. It is not evidence that eight frames is the right budget for every character or action. Do not accumulate rounded timestamps At 24 fps, one hold is 41.666… milliseconds. St

2026-09-05 原文 →
开发者

I Compared 4 Dungeon Generation Algorithms. One of Them Never Works.

Four algorithms. Same grid. Very different dungeons. I implemented BSP trees, cellular automata, random walk, and room placement, ran each one 20 times on an 80x40 grid, and measured everything: connectivity, open space, path length, speed. The Results Algorithm Open Space Connected Rooms Path Length Speed BSP Tree 42.1% 100% 1.0 105 steps 0.88 ms Cellular Automata 55.8% 0% 15.2 78 steps 52.8 ms Random Walk 35.0% 100% 1.0 73 steps 274.7 ms Room Placement 18.9% 100% 1.0 81 steps 0.29 ms The big surprise: cellular automata never produces a connected map. Zero percent connectivity across 20 runs. Every single cave system has unreachable areas. The Maps BSP Tree (structured rooms, always connected) ################################################################################ ################################################################################ #####.........#####.............###################################....#......## #####.........#####.............##..........##############........#....#......## #####...........................##..........##############....................## #####.........#####.............##..........##############.............#......## #####.........#####.............##..........##############........#....#......## ##########.#######################..........##############........#....#......## ##########.#######################..........################..################## ######..........##################..........################..################## ######..........##################..........################..######..........## ######..........##################..........################..######..........## ######..........##################..........################..######..........## ######.............###############..........################..######..........## ######..........##.###############..........################..######..........## ######..........##.###############..........################..######..........##

2026-09-05 原文 →
AI 资讯

Choosing the Right Real-Time Networking Stack for Unity in 2026

When building an online game in Unity, the question is often framed as: Should I use Photon, Netcode for GameObjects, FishNet, or Mirror? That question is too small. In 2026, there is still no single networking product that is optimal for every Unity game. The real decision is a stack : transport, netcode, authority model, session management, server hosting, and backend services. For a GameObject-based action game that needs client prediction, Photon Fusion 2.1 is a strong first PoC baseline . If your priorities are Unity Gaming Services, DOTS/ECS, self-hosting, source access, or deterministic simulation, the starting point changes. This article explains how to make that decision in production terms: latency, cheating, reconnection, hosting, bandwidth, operations, and total cost. This article uses official documentation checked on August 31, 2026 as its factual baseline. SDK versions, pricing, licensing, and service availability can change, so re-check them before committing a production project. What I mean by a real-time multiplayer game The target is roughly this class of game: 2–32 players in the same session continuously synchronized players, enemies, projectiles, or interactable objects input latency that directly affects game feel reconnects, host loss, and late joining that must be handled co-op action, FPS/TPS, racing, or competitive action If you only need turn-based play, leaderboards, chat, friends, or asynchronous PvP, you may not need a sophisticated state-synchronization netcode at all. A backend such as Nakama, PlayFab, or Unity Gaming Services may be the more important part of the architecture. Do not treat “networking” as one product A production multiplayer stack has at least five layers. Layer Responsibility Examples Transport Packet delivery, reliability, connection path, secure channel integration Unity Transport, Photon transport, UDP/WebSocket-based transports Netcode Replication, RPCs, input, prediction, interpolation, rollback Fusion, NGO,

2026-09-04 原文 →
AI 资讯

What I learned building an enemy state machine in Godot 4

I wrote "just use a match statement, it's fine" three times before I stopped saying it. It is fine, right up until an enemy needs a fourth state and two of the transitions start depending on each other. Here is what actually cost time building enemy AI for a wave-based game, in the order it bit me. Lesson 1: the match statement is fine until state 4 A two-state enemy — chase, attack — is genuinely not worth a framework: func _physics_process ( delta : float ) -> void : match state : State . CHASE : velocity = ( player . global_position - global_position ) . normalized () * speed if global_position . distance_to ( player . global_position ) < attack_range : state = State . ATTACK State . ATTACK : attack_timer -= delta if attack_timer <= 0.0 : do_attack () state = State . CHASE The moment a third and fourth state show up — hurt, dead, stagger, windup — the match block stops being one enemy's logic and becomes a grid of every state times every other state it might transition to. That grid is where the bugs live, not in any single state. Lesson 2: the bug is never inside a state, it's in the transition Every state-machine bug I actually spent time on was the same shape: state A left some flag or timer set that state C didn't know to check. An enemy stuck mid-attack-animation forever, still receiving hits, was not a bug in the attack state — it was the hurt state interrupting attack without cleaning up attack_timer or resetting the animation. The fix that made these bugs findable is giving every state an explicit enter and exit , and never mutating another state's data directly: func change_state ( new_state : State ) -> void : if new_state == state : return _exit_state ( state ) state = new_state _enter_state ( new_state ) func _exit_state ( s : State ) -> void : match s : State . ATTACK : attack_timer = 0.0 sprite . stop () func _enter_state ( s : State ) -> void : match s : State . HURT : velocity = Vector2 . ZERO hurt_timer = HURT_DURATION sprite . play ( "hurt" ) On

2026-09-04 原文 →
开发者

I built a browser game that asks your microphone to imitate a robot

I wanted a microphone project with a very small brief: hear a sound, copy it, and see how close you got. That became Mimic Party Online , a browser game where each round gives you a short sound cue and one recording attempt. The cue might be a meme clip, an animal call, a machine noise, or something that is hard to describe without making the sound yourself. It looks like a toy, and it is. It also turned into a useful little audio problem. A score based only on volume would be boring, so the game needs to compare the shape of two sounds while staying fast enough to run in a browser. The round is intentionally simple The player does five things: Choose a sound pack. Listen to the reference. Record one take. Listen to the take. Read the score. The replay is important. People tend to remember the sound they meant to make. The recording tells them what actually came out. A convincing robot alarm can turn into a tired bicycle horn pretty quickly. Quick mode runs for four rounds. Survival mode gives the player three Mic lives and keeps the run going until those lives are gone. The game also has different routes, so a player can protect a streak or accept a shorter recording window for more points. The browser does the audio work The recording stays in the browser. The game uses the microphone stream, converts the take to mono PCM at 16 kHz, and extracts the values needed for scoring. The audio does not travel to a scoring server. For each take, the extractor looks at signals such as: pitch contour timing and active duration attack and energy rhythm and onset positions spectral shape The game does not use every signal for every sound. A pitched cue cares more about contour, while a machine noise depends more on its shape and attack. A rhythmic sound needs the hits to arrive at roughly the right moments. This is also why the score is more useful when it has labels. A result of 68 is not very instructive by itself. "Timing: 74" gives you something to work on in the next atte

2026-09-04 原文 →
AI 资讯

ADAM-PS5

🎮 ADAM-PS5 — A PS5 Emulator in Development I’m working on an ambitious project called ADAM-PS5 , with the ultimate goal of developing a PlayStation 5 emulator for PC capable of running PS5 games . The project is still in the early stages of development , and I do not consider it a complete emulator at this point. I’m building the foundation step by step: system architecture, low-level emulation, memory and resource management, graphics, input handling, execution, debugging, and development tools. 🚧 Early Development There is still a huge amount of work ahead before reaching the point where commercial PS5 games can actually run. That’s why I’m sharing the project from its early stages rather than presenting it as a finished product. 🤖 One of the project’s goals is also to integrate Artificial Intelligence to help analyze errors, monitor performance, understand system logs, and assist with the development process. The long-term goal is: PC → ADAM-PS5 → PS5 Software Environment → Games Reaching that stage requires implementing and accurately simulating many different components of the console’s hardware and software architecture. I’m sharing the project now because I want to document the entire development journey from the beginning — including what gets built, what fails, what gets improved, and how the project evolves with each release. 🔥 ADAM-PS5 is not finished. It is being built. And the ultimate goal is simple: Run PlayStation 5 games on PC through our own emulator. ADAM-PS5 is an independent development project and is not affiliated with Sony Interactive Entertainment.

2026-09-03 原文 →
AI 资讯

Generating Binding Code Wasn't Enough: Moving Unity UI Composition to Compile Time

Source generators are often introduced as a way to remove boilerplate. That is useful, but it was not the main architectural reason FUI moved more of its Unity UI pipeline into Roslyn. The harder question begins after binding code has already been generated: does the runtime still need to scan assemblies, inspect attributes, resolve types, and reconstruct the relationship between a View, ViewModel, BindingContext, and Presenter? FUI's answer is to move that composition step to compile time. The generator does not stop at property notifications and binding callbacks. It also emits binding factories and strongly typed routes, so the Player runtime executes an already-validated object graph instead of rediscovering it. This article explains why that distinction matters, how the design evolved, and what the final architecture gains beyond the vague promise of “less reflection.” The original problem was repetitive protocol code Consider a settings screen with a title, a volume slider, a vibration toggle, and a close button. The ViewModel is small, but connecting it to the UI requires a surprisingly large protocol: propagate property changes to UI elements; propagate control changes back to the ViewModel; connect UI events to commands; perform initial synchronization; unsubscribe every handler during unbinding; construct the matching BindingContext and Presenter. None of these steps is individually difficult. The risk comes from repetition. A missing unsubscribe, an incompatible target member, or an incorrect string may remain invisible until that specific screen opens. The earliest code-generation experiment preserved in FUI's repository was an external FUICompiler executable. It targeted .NET 6, was published as a self-contained win-x64 tool, walked Roslyn syntax nodes, extracted binding attributes, and emitted BindingContext source. The central idea was already present: var classDeclarations = root . DescendantNodes () . OfType < ClassDeclarationSyntax >(); foreach ( v

2026-09-01 原文 →
AI 资讯

MSc Final Project DevLog #5: Tutorial and Level Design

With all of the primary mechanics sufficiently developed to allow playtesting, the next step in the project was to develop levels to teach players how to use them as well as a single level containing puzzles for players to solve using the knowledge provided in the tutorial levels. The Tutorial The game tutorial is split into five short levels. Each level imparts knowledge of one or more of the previously developed game mechanics. The number of tutorial levels was decided by listing out all the required mechanics and dividing them up across a number of levels that gave each mechanic the desired amount of attention. The goal of this was to ensure that the player was not overwhelmed with too much new information at any one time. Here is how the mechanics were divided across the five levels: Level 1: Player movement, looking around, jumping, and interacting with objects Level 2: Command generation system, Activate command, and signal blocking and range limitations Level 3: Controlling robot NPCs, target destinations, Follow command, Move To command, Cancel command, NPC-locked doors, and pressure plates Level 4: Using the Attack command against NPCs and destinations Level 5: Reflective and absorptive surfaces, signal reflection, low-frequency signals, signal penetration limits, and secret areas Tell and Show The tutorial levels all follow a pattern of tell then show. Every mechanic is explained via text on walls in the levels, and immediately followed by an opportunity or obligation to use that mechanic. For example, the first tutorial level starts with the player facing a wall displaying the controls to look around. In order to progress into the next area, the player must use those controls to turn around in order to see the way out of the starting room as well as the text explaining how to move. Similarly, at the beginning of the second tutorial level, the controls for generating an Activate command are displayed on the wall in front of the player with a door to their

2026-08-31 原文 →
AI 资讯

Hello, DEV! I'm a Game Backend Engineer

I'm a backend engineer mainly working on game servers, with Java as my primary language. Over the years, I've spent a lot of time building and debugging backend systems, and recently I've been digging deeper into concurrency, I/O, logging, and performance. Working on game servers has taught me that many problems look simple at first, but become surprisingly complicated once the system gets busy. I'll be sharing some of the things I've learned from real-world systems, including experiments, benchmarks, design decisions, and a few open-source projects I'm working on. Glad to be here. Looking forward to learning from everyone on DEV!

2026-08-31 原文 →
AI 资讯

Delta encoding multiplayer game state

Old Light is a browser strategy game where a tab can stay open for days. The client holds a full copy of the galaxy state it is allowed to see, and the server keeps that copy honest by sending patches: every change arrives as a world.delta message the client merges into what it already has. Sending changes instead of resending state is textbook delta encoding. What that leaves open is what a game state patch actually holds, and why the patch a rival receives is not the one you receive. I covered how the stream starts (one snapshot on connect, then deltas) and the time-math traps inside it in the networking post . This post is about the delta itself. What goes in a game state patch When people say delta encoding they usually mean byte diffs: compare two versions of a blob, ship the difference. That requires the sender to know which version the receiver holds. A game server broadcasting to thousands of sockets can't afford that; tracking a per-client "last known state" and diffing against it on every change would be more expensive than the update. So an Old Light delta states facts about players and sectors instead: interface WorldDelta { added ?: { players ?: Player [] }; removed ?: { playerIds ?: string [] }; updated ?: { players ?: Player []; sectors ?: Sector []; dirtySectors ?: SectorCoord []; // map data here went stale, refetch it tradeBoard ?: TradeBoardDelta ; // the market board moved deals ?: DealsDelta ; // a negotiation moved; only its two parties get this }; serverNow : number ; } A delta says a player joined, an id is gone, a player's row changed, or a sector's public map data went stale. The last two fields carry no payload. They say a surface moved, a client with that surface open goes and reads it, which keeps a busy marketplace off every socket that isn't looking at one. The server can emit the identical message to every socket without knowing what any of them currently holds, and the client can apply it to whatever it has. It also tells the rendere

2026-08-30 原文 →