[Day1] Vite 出小蜜蜂~前置准备 + 用程序画 Pixel Art!

Day1

Setup 前置准备

工欲善其事,必先利其器。
此节会先准备现代开发中不可或缺的一些工具,

Environment 环境

Bundle System 打包工具

卡比将会使用 Vite, 作为打包工具喔。

首先 建立新的专案

yarn create @vitejs/app [专案名称] --template vanilla-ts

例如.

yarn create @vitejs/app space-invaders --template vanilla-ts

进入那个专案,并开启 Dev Server

yarn 会下载这个专案有用到的所有第三方套件

cd space-invaders
yarn

接着 yarn dev 会开启 dev server

yarn dev

打开浏览器 localhost:3000,此时画面应为

hello vite

Application

打开你的 Editor,开始出发拉!

首先要先在页面上生成 <canvas /> 以方便卡比绘图,
卡比将采用 pixi.js 作为绘图引擎,

yarn add pixi.js

note. 请注意卡比此时的版本为 "pixi.js": "^6.0.4"

接着在 src/main.ts 导入,

import { Application } from "pixi.js";

接着卡比将底下预设的程序码换成

const app = new Application();

document.querySelector("#app")?.append(app.view);

此时画面应该如下,

hello pixi.js

程序码如下,

-- src/main.ts

import { Application } from "pixi.js";

const app = new Application();

document.querySelector("#app")?.append(app.view);

Pixel Art Assets 点阵图资源

分析

那个年代并没有绘图软件,图片都是由工程师透过程序绘成,
所以卡比首先要思考如何用程序绘制点阵图形。

卡比试着画出,这个游戏的代表外星人 Crab

Crab

可以简单看出 Crab 由高度 8 pixels 跟宽度 11 pixels 的点阵图构成,且只有一个颜色。

像这样的图形可以透过 2维阵列 来记录整张图片点阵资料,
简单的方式来记录每个格子里面有没有颜色即可,
例如 1 - 有颜色, 0 - 没有颜色。

故可以用以下表示这张图片,

const image = [
  [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
  [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
  [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
  [0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
  [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1],
  [0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0],
];

然後试着用上面的资料绘制点阵图。

实作

接者卡比要用 pixi.js 中的 Graphics API 进行点阵图绘制,

- import { Application } from "pixi.js";
+ import { Application, Graphics } from "pixi.js";

生成物件

const graphics = new Graphics();

遍历 image 阵列,透过资料来绘制图形,

if (image[y][x] === 0) continue; 0 的话就不画颜色,直接跳过。

beginFill(0xffffff) 是指先选好要上的颜色,0xffffff 就是白色的 16 进位色码。

drawRect(x, y, w, h) 是指在某个位置画一个特定宽高的矩形。

endFill() 画完形状并涂上颜色。

for (let y = 0; y < image.length; y++) {
  for (let x = 0; x < image[y].length; x++) {
    if (image[y][x] === 0) continue;

    graphics.beginFill(0xffffff);

    graphics.drawRect(x, y, 1, 1);

    graphics.endFill();
  }
}

最後将这个图形放到画面上即可

app.stage.addChild(graphics);

此时画面应该如下

hello crab first

似乎有点小,将画面的比例调整一下,
宽度 11 px, 高度 8 px, 放大 10 倍 来看看

- const app = new Application();
+ const app = new Application({
+   width: 11,
+   height: 8,
+   resolution: 10,
+ });

就这样卡比画出 Crab 了。

hello crab final

程序码如下,

-- src/main.ts

import { Application, Graphics } from "pixi.js";

const app = new Application({
  width: 11,
  height: 8,
  resolution: 10,
});

document.querySelector("#app")?.append(app.view);

const image = [
  [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
  [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
  [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
  [0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
  [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1],
  [0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0],
];

const graphics = new Graphics();

for (let y = 0; y < image.length; y++) {
  for (let x = 0; x < image[y].length; x++) {
    if (image[y][x] === 0) continue;

    graphics.beginFill(0xffffff);

    graphics.drawRect(x, y, 1, 1);

    graphics.endFill();
  }
}

app.stage.addChild(graphics);

Refactor 重构

接下来卡比要将这段Crab的程序码整理到一个地方放,
方便以後卡比要再画出Crab时可以直接使用,而不需要重新走过上面的思考流程。

首先,卡比在 src 底下建立 characters 的资料夹,并建立一只新的档案叫 Crab.ts
并把画出 Crab 的相关程序码搬到那个地方。

-- src/characters/Crab.ts

import { Graphics } from "pixi.js";

const image = [
  [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0],
  [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
  [0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0],
  [0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1],
  [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1],
  [0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0],
];

export default function Crab() {
  const graphics = new Graphics();

  for (let y = 0; y < image.length; y++) {
    for (let x = 0; x < image[y].length; x++) {
      if (image[y][x] === 0) continue;

      graphics.beginFill(0xffffff);

      graphics.drawRect(x, y, 1, 1);

      graphics.endFill();
    }
  }

  return graphics;
}

之後来测试一下 Crab 函式。

-- src/main.ts

import "./style.css";
import { Application } from "pixi.js";
import Crab from "./characters/Crab";

const app = new Application({
  width: 11,
  height: 8,
  resolution: 10,
});

document.querySelector("#app")?.append(app.view);

app.stage.addChild(Crab());

这样卡比成功重构了Crab

Other Characters 其他角色

因为接下来的角色作法跟上面的 Crab 差不多,
卡比直接提供其他角色的资料以供大家做练习。

以下练习可以自行调整 Application 的画布大小喔。

LaserCannon

const laserCannon = [
  [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
  [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];

LaserCannon

Octopus

const octopus = [
  [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
  [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1],
  [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
  [0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0],
  [0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0],
  [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
];

Octopus

Squid

const squid = [
  [0, 0, 0, 1, 1, 0, 0, 0],
  [0, 0, 1, 1, 1, 1, 0, 0],
  [0, 1, 1, 1, 1, 1, 1, 0],
  [1, 1, 0, 1, 1, 0, 1, 1],
  [1, 1, 1, 1, 1, 1, 1, 1],
  [0, 0, 1, 0, 0, 1, 0, 0],
  [0, 1, 0, 1, 1, 0, 1, 0],
  [1, 0, 1, 0, 0, 1, 0, 1],
];

Squid

小考题

  1. 请问假如要在场上同时放上 CrabLaserCannonOctopusSquid,要怎麽做呢?
    提示:查看 Graphic 文件,position,因为我希望大家可以去看文件。

关於兔兔们:


<<:  Day03 - 使用 Google Compute Engine 建立 VM

>>:  建立 Google App Script 专案(2)

Day 20. Hashicorp Nomad: Docker driver image behavior

Hashicorp Nomad: Docker driver image behavior 在Has...

Day#20 Dependencies & conversation UI

前言 今天来加入更多的Dependencies,以及聊天开发的准备 Cocoapods 那麽我们先来...

企划实现(24)

使用tablatout串fragment xml程序码 <?xml version="...

[Day15] 碰撞侦测 - 分离轴原理 SAT

今日目标 实作SAT碰撞侦测 SAT的作法 回顾一下,AABB的作法是不管是甚麽形状,都把物件包进矩...

[Day02] 第二章- 初探金流API文件-1

前言 今天会分享我的阅读api心得 并做简易的操作来使用今天研读後的成果 前几日会以阅读文件跟熟悉永...