PreloadScene:程序化纹理生成与加载条动画

举报
yd_296221794 发表于 2026/09/12 11:45:08 2026/09/12
【摘要】 > 没有一张外部图片,却生成了玩家、敌人、子弹三张贴图。这是怎么做到的?## 场景概览`PreloadScene` 是 NEON RUNNER 2077 里最"有料"的场景——它不加载任何外部资源,却用 `make.graphics` 程序化生成了 3 张贴图,还做了一个看起来像在"加载"的进度条。```ts// src/scenes/PreloadScene.tsexport class ...

> 没有一张外部图片,却生成了玩家、敌人、子弹三张贴图。这是怎么做到的?


## 场景概览


`PreloadScene` 是 NEON RUNNER 2077 里最"有料"的场景——它不加载任何外部资源,却用 `make.graphics` 程序化生成了 3 张贴图,还做了一个看起来像在"加载"的进度条。


```ts

// src/scenes/PreloadScene.ts

export class PreloadScene extends Phaser.Scene {

  private progressBar!: Phaser.GameObjects.Rectangle;

  private progressText!: Phaser.GameObjects.Text;

  private percentText!: Phaser.GameObjects.Text;

  private barWidth = 480;


  preload(): void {

    this.generateProceduralAssets();

  }


  create(): void {

    // 创建进度条、加载消息、动画

  }


  private generateProceduralAssets(): void {

    // 生成 player / enemy / bullet 三张贴图

  }

}

```


## 为什么不用外部图片


在 2026 年,做霓虹射击游戏完全可以找一张 PNG 素材。但 NEON RUNNER 2077 选择程序化生成,有三个原因:


1. **零网络依赖**:没有外部图片,没有 CDN,没有 404。双击 `index.html` 就能跑。

2. **风格可控**:程序化生成的纹理是几何图形,天然契合霓虹风格。如果用照片素材,风格会"出戏"。

3. **构建简单**:不需要图片压缩、不需要 WebP 转换、不需要 retina 适配。


## 程序化纹理生成:三张贴图


### 玩家纹理(三角形战机)


```ts

const playerGraphics = this.make.graphics({ x: 0, y: 0 }, false)!;

playerGraphics.fillStyle(0xfcee0a, 1);

playerGraphics.beginPath();

playerGraphics.moveTo(0, -16);

playerGraphics.lineTo(12, 12);

playerGraphics.lineTo(0, 6);

playerGraphics.lineTo(-12, 12);

playerGraphics.closePath();

playerGraphics.fillPath();

playerGraphics.lineStyle(2, 0xffffff, 0.9);

playerGraphics.strokePath();

playerGraphics.generateTexture('player', 32, 36);

playerGraphics.destroy();

```


这是一个**霓虹黄的三角形**,顶点在 (0, -16),底边在 (±12, 12)。`fillPath()` 填充黄色,`strokePath()` 用白色描边。`generateTexture('player', 32, 36)` 把这个图形导出为 32×36 的 Canvas 纹理,键名是 `'player'`。


### 敌人纹理(红色圆环)


```ts

const enemyGraphics = this.make.graphics({ x: 0, y: 0 }, false)!;

enemyGraphics.fillStyle(0xff003c, 1);

enemyGraphics.fillCircle(0, 0, 14);

enemyGraphics.lineStyle(2, 0xbd00ff, 1);

enemyGraphics.strokeCircle(0, 0, 14);

enemyGraphics.fillStyle(0xffffff, 1);

enemyGraphics.fillCircle(0, 0, 4);

enemyGraphics.generateTexture('enemy', 32, 32);

enemyGraphics.destroy();

```


红色填充圆(半径 14),紫色描边,中心一个白色小圆点。这个设计让敌人看起来像"被锁定的目标"——红色代表危险,紫色描边增加层次感。


### 子弹纹理(青色矩形)


```ts

const bulletGraphics = this.make.graphics({ x: 0, y: 0 }, false)!;

bulletGraphics.fillStyle(0x00f0ff, 1);

bulletGraphics.fillRoundedRect(-2, -8, 4, 16, 2);

bulletGraphics.generateTexture('bullet', 4, 16);

bulletGraphics.destroy();

```


青色圆角矩形,4×16 像素。`fillRoundedRect(-2, -8, 4, 16, 2)` 的锚点在 (-2, -8),所以子弹的中心在 (0, 0)——这是 Phaser 里精灵的默认原点。


## 加载条动画:看起来像在"加载"


`PreloadScene` 的 `create()` 里做了一个 1800ms 的 `tweens.addCounter` 动画,让进度条从 0% 走到 100%。但关键不是"走完",而是**加载消息跟着进度变化**:


```ts

const LOAD_MESSAGES = [

  '> initializing neon runtime...',

  '> loading cyberware modules...',

  '> calibrating glitch shaders...',

  '> syncing holo interface...',

  '> arming neon projectiles...',

  '> system ready.',

];

```


```ts

onUpdate: (_tween, _key, _target, current) => {

  const value = current / 100;

  this.progressBar.width = this.barWidth * value;

  this.percentText.setText(`${Math.floor(value * 100)}%`);

  const idx = Math.min(LOAD_MESSAGES.length - 1, Math.floor(value * LOAD_MESSAGES.length));

  this.progressText.setText(LOAD_MESSAGES[idx]);

}

```


`Math.floor(value * LOAD_MESSAGES.length)` 把 0-100 的进度映射到 0-5 的消息索引。进度 0% 显示第一条,17% 显示第二条,33% 显示第三条……100% 显示最后一条。


这个"消息跟着进度走"的设计,让玩家觉得"真的在加载什么"——尽管实际上什么都没加载。这是一种**感知设计**:用文字营造"系统在做事"的感觉。


## 加载完成后的过渡


```ts

onComplete: () => {

  this.time.delayedCall(300, () => {

    this.cameras.main.fadeOut(500, 10, 10, 15);

    this.cameras.main.once('camerafadeoutcomplete', () => {

      this.scene.start('MenuScene');

    });

  });

}

```


进度条走完后,再等 300ms,然后相机淡出(500ms,RGB 10,10,15),淡出完成后切到 `MenuScene`。这个"等 300ms"是为了让玩家看到 "> system ready." 这条消息——如果立刻切走,玩家会觉得"加载完了?还没看清呢"。


## 下一章预告


`PreloadScene` 讲完了,但 NEON RUNNER 2077 的视觉风格真正爆发的地方是 `MenuScene`——故障文字、扫描线、全息面板、键盘/鼠标双输入。下一章我会拆解这个 214 行的场景。


> 程序化纹理的核心原则:**能用代码生成的,就不要用文件**。零依赖、零网络、零风格冲突。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。