PageSourceSearch

https://player3.gg/assets/SdkAchievementsApi-Cu3-_iUj.js

js player3.gg collected 2026-10-05 03:11:42 UTC 11,090 bytes, 82 lines download raw bytes

1import{u as r,j as e}from"./index-yLHt_u4B.js";import{a,S as t}from"./SectionHeading-B6aRHkIu.js";import{I as o}from"./InfoCard-BaeDCBld.js";import{A as s}from"./ApiMethodCard-BgUvn-qY.js";import{C as n}from"./CodeBlock-BImFnJQR.js";const h=()=>(r({title:"Achievements API | PIX3L SDK | Player 3",description:"PIX3L.achievements runtime API reference — report incremental and absolute progress, list achievements, and celebrate unlocks in-game.",canonical:"https://player3.gg/pix3l-sdk/achievements-api"}),e.jsxs(a,{children:[e.jsx(t,{id:"achievements",icon:"emoji_events",title:"Achievements"}),e.jsxs("p",{className:"text-muted-foreground leading-relaxed",children:[e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"PIX3L.achievements"})," ","is the runtime API your game uses to report progress. You author achievements in the Studio dashboard, then report progress from gameplay using the achievement's stable"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"key"}),". Player 3 validates every report server-side and unlocks automatically when the target is reached."]}),e.jsxs("div",{className:"rounded-md border border-border bg-secondary/40 p-4 text-sm text-muted-foreground leading-relaxed",children:["New here? Read the"," ",e.jsx("a",{href:"/pix3l-sdk/achievements",className:"text-primary font-medium hover:underline",children:"Achievement System overview"})," ","first to learn how to define achievements and choose the right mode (increment, absolute, boolean)."]}),e.jsxs("div",{className:"grid sm:grid-cols-3 gap-4",children:[e.jsx(o,{icon:"bolt",title:"Client Reports Progress",children:"Your game reports progress against an achievement's key. Reports are queued locally, then flushed to Player 3."}),e.jsx(o,{icon:"schedule",title:"3-sec Auto-Batching",children:"Events are automatically coalesced and flushed every 3 seconds (or when the buffer fills), minimizing network chatter."}),e.jsx(o,{icon:"verified_user",title:"Server-Side Unlocks",children:"The platform validates each report, enforces rate limits, and unlocks atomically. Clients cannot forge unlocks."})]}),e.jsx(t,{id:"ach-methods",icon:"terminal",title:"Methods"}),e.jsxs("div",{className:"space-y-4",children:[e.jsx(s,{signature:"PIX3L.achievements.progress(key, amount?)",badge:"void",description:"Reports incremental progress toward an achievement. Use this for counters like coins collected, enemies defeated, or matches played. Deltas are added to the running total server-side.",params:[{name:"key",type:"string",description:"The achievement key defined in the Studio dashboard."},{name:"amount",type:"number",description:"Positive integer to add to the counter.",default:"1"}],notes:"Works with achievements in `increment` mode. Multiple calls in the same 3-second window are coalesced into a single sum.",example:{title:"Report incremental progress",code:`// Player picked up a coin
2PIX3L.achievements.progress("coins_1000");
3
4// Player picked up 25 coins from a chest
5PIX3L.achievements.progress("coins_1000", 25);
6
7// Player defeated an enemy
8PIX3L.achievements.progress("enemies_100");`}}),e.jsx(s,{signature:"PIX3L.achievements.setProgress(key, value)",badge:"void",description:"Reports an absolute value. Use this for high scores, current level, or longest survival time. The server keeps the maximum ever reported (monotonic max), smaller values are ignored.",params:[{name:"key",type:"string",description:"The achievement key defined in the Studio dashboard."},{name:"value",type:"number",description:"The current absolute value (e.g. score, level)."}],notes:"Works with achievements in `absolute` mode. Safe to call every frame, the SDK coalesces to the highest value in the current window.",example:{title:"Report a high score",code:`// On score change
9function onScoreChange(score) {
10  PIX3L.achievements.setProgress("high_score", score);
11}
12
13// On level up
14function onLevelUp(level) {
15  PIX3L.achievements.setProgress("reach_level_50", level);
16}`}}),e.jsx(s,{signature:"PIX3L.achievements.list()",badge:"async",description:"Fetches every achievement defined for the current game, along with the current player'
16s progress and unlock status.",returns:[{name:"AchState[]",type:"Promise<AchState[]>",description:"Array of achievement state records."}],example:{title:"Render an achievements screen",code:`const achievements = await PIX3L.achievements.list();
17
18for (const a of achievements) {
19  console.log(a.title, a.progress + "/" + a.target, a.unlocked ? "✓" : "");
20}`}}),e.jsx(s,{signature:"PIX3L.achievements.onUnlock(callback)",badge:"() => void",description:"Subscribes to unlock events so your game can play a sound, show a custom celebration, or trigger a cutscene. Player 3 also renders its own themed toast automatically.",params:[{name:"callback",type:"(ach: AchState) => void",description:"Fires once per achievement, the moment it unlocks."}],returns:[{name:"unsubscribe",type:"() => void",description:"Call to remove the listener."}],example:{title:"Play a custom unlock sound",code:`const off = PIX3L.achievements.onUnlock((ach) => {
21  console.log("Unlocked:", ach.key, ach.title);
22  playSfx("fanfare");
23  showCustomBanner(ach.title);
24});
25
26// Later, on scene teardown:
27off();`}})]}),e.jsx(t,{id:"ach-types",icon:"data_object",title:"TypeScript Reference"}),e.jsxs("p",{className:"text-sm text-muted-foreground leading-relaxed",children:["The ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"AchState"})," ","interface is returned by"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"list()"})," and passed to"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"onUnlock"})," callbacks."]}),e.jsx(n,{title:"AchState.d.ts",language:"typescript",code:`type AchMode = "increment" | "absolute" | "boolean";
28
29interface AchState {
30  /** Stable identifier used to report progress. */
31  key: string;
32  /** Display title from the Studio dashboard. */
33  title: string;
34  /** Optional description shown in achievement lists. */
35  description: string;
36  /** Progress mode chosen when authoring the achievement. */
37  mode: AchMode;
38  /** Target value required to unlock. */
39  target: number;
40  /** Current player progress toward the target. */
41  progress: number;
42  /** True once progress >= target. */
43  unlocked: boolean;
44  /** ISO timestamp of the unlock, or null if still locked. */
45  unlockedAt: string | null;
46  /** Whether the achievement is hidden until unlocked. */
47  hidden: boolean;
48  /** Optional resolved icon URL for the achievement. */
49  iconUrl: string | null;
50}
51
52interface Pix3lAchievements {
53  progress(key: string, amount?: number): void;
54  setProgress(key: string, value: number): void;
55  list(): Promise<AchState[]>;
56  onUnlock(cb: (ach: AchState) => void): () => void;
57}`}),e.jsx(t,{id:"ach-tips",icon:"lightbulb",title:"Developer Tips"}),e.jsxs("div",{className:"p-5 rounded-md border border-primary/40 bg-primary/5 space-y-2",children:[e.jsxs("h4",{className:"font-display font-semibold text-foreground",children:["No ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"unlock()"})," method by design"]}),e.jsxs("p",{className:"text-sm text-muted-foreground leading-relaxed",children:["There is intentionally no client-side"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"unlock()"})," call. Unlocks happen automatically on the server the moment progress reaches the target. For"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"boolean"})," ","achievements, any positive report unlocks them, call"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:'progress("secret_room")'})," ","once."]})]}),e.jsxs("div",{className:"p-5 rounded-md border border-border bg-card space-y-2",children:[e.jsx("h4",{className:"font-display font-semibold text-foreground flex items-center gap-2",children:"Offline & standalone fallback"}),e.jsxs("p",{className:"text-sm text-muted-foreground leading-relaxed",children:["When your game runs outside the Player 3 runtime (e.g. local development, a standalone build, or an unhosted preview),"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"PIX3L.achievements"})," ","degrades gracefully, calls become no-ops, and"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"list()"})," resolves with an empty array. You never need to guard calls with"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"if (PIX3L)"}),"."]})]}),e.jsxs("div",{className:"p-5 rounded-md border border-border bg-card space-y-2",children:[e.jsx("h4",{className:"font-display font-semibold text-foreground",children:"Rate limiting & safeguards"}),e.jsxs("ul",{className:"text-sm text-muted-foreground space-y-1.5 list-disc list-inside",children:[e.jsxs("li",{children:["Events are buffered locally and flushed every ",e.jsx("strong",{className:"text-foreground",children:"3 seconds"})," or when the buffer reaches ",e.jsx("strong",{className:"text-foreground",children:"32 events"}),"."]}),e.jsxs("li",{children:["Repeated"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"progress()"})," calls for the same key are summed; repeated"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"setProgress()"})," ","calls keep the max."]}),e.jsxs("li",{children:["The server enforces per-player rate limits and caps per-report deltas via each achievement's"," ",e.jsx("code",{className:"font-mono text-xs px-1 py-0.5 rounded bg-secondary text-foreground",children:"max_step"}),"."]}),e.jsx("li",{children:"Already-unlocked achievements silently ignore further reports."})]})]}),e.jsx(t,{id:"ach-example",icon:"integration_instructions",title:"Full Example"}),e.jsx(n,{title:"Wiring achievements into gameplay",code:`// Celebrate unlocks with a custom sound + banner
58const offUnlock = PIX3L.achievements.onUnlock((ach) => {
59  playSfx("fanfare");
60  showBanner(\`Unlocked: \${ach.title}\`);
61});
62
63// Increment counters as gameplay happens
64function onCoinPickup(count = 1) {
65  PIX3L.achievements.progress("coins_1000", count);
66}
67
68function onBossDefeated() {
69  // Boolean achievement — one call is enough
70  PIX3L.achievements.progress("first_boss");
71}
72
73// Absolute values — safe to call on every score change
74function onScore(score) {
75  PIX3L.achievements.setProgress("high_score", score);
76}
77
78// Render an in-game achievements screen
79async function openAchievementsMenu() {
80  const list = await PIX3L.achievements.list();
81  renderAchievementList(list);
82}`})]}));export{h as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.