> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/all-out-docs/docs-zh/jiao-ben-bian-xie/syntax.md).

# 开始使用 CSL

CSL 是 All Out 的自定义脚本语言。它是静态类型的，感觉最像 Go/Odin —— 除了 **游戏状态会自动从服务器同步到客户端**.

{% hint style="info" %}
你不需要编写 RPC、SyncVar 或自定义复制。我们会为你处理这些！
{% endhint %}

## 你的第一个脚本（`main.csl`)

当你创建一个新项目时，All Out 会生成一个 `main.csl` 在你的项目的 `scripts/` 文件夹。

```go
import "core:ao"

// ============================================================================
// 全局生命周期
// ============================================================================

ao_before_scene_load :: proc() {
    // 注册物品定义、货币等。
    // 在场景创建之前运行。
}

ao_start :: proc() {
    // 场景开始时调用一次。
}

ao_update :: proc(dt: float) {
    // 每帧调用。
}

ao_late_update :: proc(dt: float) {
    // 在 ao_update 之后每帧调用。
}

// ============================================================================
// 玩家生命周期
// ============================================================================

Player :: class : Player_Base {
    ao_start :: method() {
    }

    center := entity.world_position;
    }

    ao_late_update :: method(dt: float) {
    }

    ao_end :: method() {
    }
}
```

例如，在每个玩家加入时记录一条消息：

```go
Player :: class : Player_Base {
    ao_start :: method() {
        log_info("hello %", {this.get_username()});
    }
}
```

如果你想更深入了解这些函数何时运行，请参见 [游戏/帧生命周期](/all-out-docs/docs-zh/jiao-ben-bian-xie/game-frame-lifecycle.md).

## 导入

你的 `main.csl` 应当导入 `core:ao` 以及你创建的任何文件夹（例如 `ui/`, `abilities/`等）。

```go
import "core:ao"
import "ui"
```

导入指向的是文件夹，而不是单个文件。文件夹导入会包含该 `.csl` 文件夹中的文件。该文件夹内的导入会相对于它来解析：

```go
// main.csl
import "core:ao"
import "abilities"

// abilities/projectiles.csl
import "helpers"
```

{% hint style="warning" %}
在大多数项目中， **只在 `main.csl` 导入**。不要把导入分散到很多文件里——最终你会遇到令人困惑的顺序/可见性问题。
{% endhint %}

## 声明（变量和常量）

声明将一个名称绑定到一个值。

### 变量

```go
// 通用形式
<name>: <type> = <expression>;
```

\<type> `<expression>` 或 `都可以省略：` // 显式类型

```go
my_value: int = 42;
// 类型推断

my_value := 42; // 推断为 int
// 零初始化（与 my_value := 0; 相同）

my_value: int;
常量
```

### 常量使用

并且必须是编译期常量。它们可以是标量、字符串、类型、过程值、数组或复合字面量： `::` MAX\_PLAYERS :: 12;

```go
Spawn_Desc :: struct {

pos: v2;
    name: string;
    DEFAULT_SPAWNS: []Spawn_Desc : {
}

{"Blue", {-4, 0}},
    {"Red", {4, 0}},
    这是无效的（因为
};
```

a `不是编译期常量）：` a := 123;

```go
b :: a; // 编译错误
全局变量初始化器也必须是编译期常量。使用
```

ao\_before\_scene\_load `进行运行时初始化。` 或 `ao_start` 全局变量

### 全局变量使用与局部变量相同的声明语法。它们可以保持零初始化，或者用编译期常量初始化，包括结构体、数组、过程值，以及

值： `typeid` score\_total: int;

```go
spawn_names: []string = {"Blue", "Red"};
default_type: typeid = int;
start_round :: proc() {

on_match_start: proc() = start_round;
}

全局变量是可变的，并且在脚本实例的生命周期内持续存在。不要把它们用于按玩家区分的游戏状态。
```

类型

## 别名：

### 原始类型

* 有符号整数： `s8`, `s16`, `s32`, `s64`
* 无符号整数： `u8`, `u16`, `u32`, `u64`
* 布尔值： `bool`
* 浮点数： `f32`, `f64`
* 向量：
  * `int` == `s64`
  * `uint` == `u64`
  * `float` == `f32`
* 任意 `v2`, `v3`, `v4`
* `string`
* `typeid`
* `向量类型`

### 具有

`v2` .x `.y`, `会增加`; `v3` .z `.w`; `v4` .z `—— 所有字段都是 float：` pos: v2 = {10, 20};        // x=10, y=20

```go
color: v4 = {1, 0, 0, 1};  // 红色，alpha=1
offset := v3{1, 4, 9};     // 类型推断
结构体和类
```

## 结构体是

值类型 **（赋值时会复制）。类是** 引用类型 **（你使用** new `来分配它们）`).

### 结构体（值类型）

```go
Food_Definition :: struct {
    name: string;
    food_value: int;
}

food: Food_Definition;
food.name = "Apple";
food.food_value = 10;
```

### 类（引用类型）

```go
Foo :: class {
    value: int = 10;
    position: v2 = {12, 34};
}

foo := new(Foo);
```

类字段可以有默认值。派生类可以覆盖继承的默认值，而无需重新声明该字段：

```go
Enemy :: class {
    health: int = 100;
    speed: float = 3.0;
}

Boss :: class : Enemy {
    health = 500;
    speed = 1.5;
}
```

### 继承

结构体/类可以从其他结构体/类继承：

```go
Animal :: class {
    name: string;
    age: int;
}

Dog :: class : Animal {
    breed: string;
}

dog := new(Dog);
dog.name = "Buddy";
dog.age = 5;
dog.breed = "Labrador";
```

## 过程和方法

### 过程（`proc`)

```go
add :: proc(a: int, b: int) -> int {
    return a + b;
}

result := add(2, 4); // 6
```

过程是普通值，可以像任何其他值一样赋值/存储：

```go
op := proc(a: int, b: int) -> int { return a + b; };
op = proc(a: int, b: int) -> int { return a * b; };
```

### 方法（`method`)

使用 `method()` ）定义在结构体/类内部。方法带有一个隐式的 `this` 引用参数。

```go
Dog :: class {
    name: string;

    bark :: method() {
        log_info("% says bark!", {name});
    }
}

dog := new(Dog);
dog.name = "Buddy";
dog.bark();
```

### 字段访问与方法调用

使用 `.` 对字段和方法都适用：

```go
hp := player.health;        // 字段访问
player.respawn();          // 方法调用
entity.set_local_scale({2, 2});
```

{% hint style="info" %}
只要任意过程的第一个参数与接收者类型匹配，就可以作为“方法”调用。真正的方法以及类型上的过程值字段，会优先于 CSL 回退到匹配的自由过程。
{% endhint %}

## 数组

CSL 有几种你会经常使用的“类数组”类型：

* **固定数组**: `[4]int`
* **切片 / 托管数组**: `[]T` （通常用作数组的“只读视图”）
* **动态数组**: `[..]T` （可调整大小的列表）
* **非托管数组**: `[^]T` （用于内置 API 签名，例如 `format_string`, `log_info`等——将值作为 `{a, b, c}`)

动态数组暴露 `.data`, `.count`以及 `.capacity`，并使用方法调用语法进行操作：

```go
numbers: [..]int;
numbers.append(10);
numbers.append(20);

log_info("count: %", {numbers.count});
log_info("first: %", {numbers[0]});
```

完整指南（包括删除模式）请参见 [数组和集合](/all-out-docs/docs-zh/jiao-ben-bian-xie/arrays-and-collections.md).

## 控制流

### 如果 / 否则

```go
if hp <= 0 {
    die();
} else if hp < 25 {
    Notifier.notify(player, "Low health!");
} else {
    // 一切正常
}
```

### switch

使用 `default:` 用于默认分支。case 支持 **多个值** （以逗号分隔）和 **范围**。没有 C 风格的贯穿。

```go
Item_Tier :: enum {
    COMMON;
    UNCOMMON;
    RARE;
    EPIC;
    LEGENDARY;
}

get_tier_color :: proc(tier: Item_Tier) -> v4 {
    switch tier {
        case .COMMON:              return {0.7, 0.7, 0.7, 1.0};
        case .UNCOMMON:            return {0.3, 0.8, 0.3, 1.0};
        case .RARE, .EPIC:         return {0.3, 0.5, 1.0, 1.0};
        case .LEGENDARY:           return {1.0, 0.8, 0.2, 1.0};
        default:                   return {1, 1, 1, 1};
    }
}

// 范围 case
get_difficulty :: proc(level: int) -> string {
    switch level {
        case 1..5:          return "Easy";
        case 6..<11:        return "Medium";
        case 11..20, 25:    return "Hard";
        default:            return "Unknown";
    }
}
```

`..` 包含两个端点。 `..<` 不包含上端点。

### while / for

```go
while condition {
    // ...
}

// 闭区间范围：0..9 包含 9
for i: 0..9 {
    log_info("i=%", {i});
}

// 左闭右开范围：0..<count 不包含 count
for i: 0..<items.count {
    log_info("item %", {items[i]});
}

// 反向迭代
for i: 0..<items.count #reverse {
    log_info("reverse item %", {items[i]});
}

// 遍历数组/切片元素
for item: my_items {
    // ...
}

// 带索引变量
for item, i: my_items {
    log_info("item % at index %", {item, i});
}

// 遍历自定义迭代器（实体/组件中常见）
for enemy: component_iterator(Enemy) {
    enemy.update_ai();
}
```

基于自定义迭代器的 `for` 循环需要一个 `next :: method() -> bool` 和一个 `current` 字段。

## 类型转换

使用 `expr.(T)` 或 `cast(T)expr` 转换为：

```go
a := 123.4;
b := a.(int);   // 123
c := cast(float)b; // 123.0
```

当目标类型已经已知时，你可以让 CSL 推断它：

```go
i: int = a.();
j: int = cast a;
```

## 通过引用传递： `ref` （推荐）

当你需要修改参数时， **优先使用 `ref`** 而不是原始指针。

```go
update_position :: proc(pos: ref v2, velocity: v2, dt: float) {
    pos.x += velocity.x * dt;
    pos.y += velocity.y * dt;
}

my_pos := v2{0, 0};
update_position(ref my_pos, {10, 5}, 0.16);
```

## 回调：函数指针 + userdata（无闭包）

CSL 没有闭包。内联的 `proc(...) { ... }` 不能捕获周围变量。

要携带上下文，请将回调与一个 `userdata: Object` 字段配对：

```go
Player :: class : Player_Base {
    on_death_userdata: Object;
    on_death: proc(player: Player, userdata: Object);

    die :: method() {
        if on_death != null {
            on_death(this, on_death_userdata);
        }
    }
}

Death_Tracker :: class : Component {
    count: int;

    ao_start :: method() {
        player := entity.get_component(Player);
        player.on_death_userdata = this;
        player.on_death = proc(player: Player, userdata: Object) {
            tracker := userdata.(Death_Tracker);
            tracker.count += 1;
        };
    }
}
```

## 类型信息（类型作为值）

`typeid` 值可以传递给多态过程：

```go
default_of :: proc($T: typeid) -> T {
    t: T;
    return t;
}

a := default_of(int);      // 0
b := default_of(string);   // ""
c := default_of([4]int);   // {0, 0, 0, 0}
```

## 最佳实践（All Out 中的 CSL）

* **避免全局游戏状态。** 多个玩家连接时——把每个玩家的状态存到 `Player` 上。
* **将视觉效果逻辑与游戏逻辑分离。** 使用 `is_local()` 用于仅本地的 UI/粒子效果， `is_local_or_server()` 用于必须在服务器 + 本地客户端上运行的游戏输入。
* **以移动端优先为默认。** 除非你的游戏明确面向 PC，否则不要依赖键盘/鼠标输入。
* **如果你对语法或 API 不确定**，请查看 `api_reference/` 在你的项目中生成的文件夹（其中包含最新的 `core.csl` 表面层）。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.allout.game/all-out-docs/docs-zh/jiao-ben-bian-xie/syntax.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
