mirror of
https://github.com/unanmed/HumanBreak.git
synced 2025-01-31 23:29:27 +08:00
feat: 地图渲染基本完成
This commit is contained in:
parent
117bd94928
commit
78efe81423
@ -40,7 +40,7 @@ var enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80 =
|
||||
"angel": {"name":"天使","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||
"elemental": {"name":"元素生物","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||
"steelGuard": {"name":"铁守卫","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[18],"value":20},
|
||||
"evilBat": {"name":"邪恶蝙蝠","hp":1000,"atk":800,"def":350,"money":1,"exp":40,"point":0,"special":[2],"bigImage":"bear.png"},
|
||||
"evilBat": {"name":"邪恶蝙蝠","hp":1000,"atk":800,"def":350,"money":1,"exp":40,"point":0,"special":[2],"bigImage":null},
|
||||
"frozenSkeleton": {"name":"冻死骨","hp":7500,"atk":2500,"def":1000,"money":2,"exp":90,"point":0,"special":[1,20],"crit":500,"ice":10,"description":"弱小,无助,哀嚎,这就是残酷的现实。哪怕你身处极寒之中,也难有人对你伸出援手。人类总会帮助他人,但在这绝望的环境下,人类的本性便暴露无遗。这一个个精致却又无情的骷髅,便是那些在极寒之中死去的冤魂。"},
|
||||
"silverSlimelord": {"name":"银怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||
"goldSlimelord": {"name":"金怪王","hp":0,"atk":0,"def":0,"money":0,"exp":0,"point":0,"special":[]},
|
||||
|
@ -32,6 +32,7 @@ var functions_d6ad677b_427a_4623_b50f_a445a3b0ef8a = {
|
||||
// 初始化地图
|
||||
core.status.floorId = floorId;
|
||||
core.status.maps = maps;
|
||||
core.extractBlocks(floorId);
|
||||
core.maps._resetFloorImages();
|
||||
// 初始化怪物和道具
|
||||
core.material.enemys = core.enemys.getEnemys();
|
||||
|
@ -73,7 +73,7 @@ var maps_90f36752_8815_4be8_b32b_d7fad1d0542e =
|
||||
"83": {"cls":"animates","id":"redDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"redKey":1}},"name":"红门"},
|
||||
"84": {"cls":"animates","id":"greenDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"greenKey":1}},"name":"绿门"},
|
||||
"85": {"cls":"animates","id":"specialDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"specialKey":1}},"name":"机关门"},
|
||||
"86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}},"name":"铁门","bigImage":"bear.png"},
|
||||
"86": {"cls":"animates","id":"steelDoor","trigger":"openDoor","animate":1,"doorInfo":{"time":160,"openSound":"door.mp3","closeSound":"door.mp3","keys":{"steelKey":1}},"name":"铁门","bigImage":null},
|
||||
"87": {"cls":"terrains","id":"upFloor","canPass":true},
|
||||
"88": {"cls":"terrains","id":"downFloor","canPass":true},
|
||||
"89": {"cls":"animates","id":"portal","canPass":true},
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { EmitableEvent, EventEmitter } from '../common/eventEmitter';
|
||||
import { logger } from '../common/logger';
|
||||
import { MotaOffscreenCanvas2D } from '../fx/canvas2d';
|
||||
import { SizedCanvasImageSource } from './preset/misc';
|
||||
|
||||
// 经过测试(https://www.measurethat.net/Benchmarks/Show/30741/1/drawimage-img-vs-canvas-vs-bitmap-cropping-fix-loading)
|
||||
// 得出结论,ImageBitmap和Canvas的绘制性能不如Image,于是直接画Image就行,所以缓存基本上就是存Image
|
||||
@ -39,6 +41,27 @@ interface TextureRequire {
|
||||
images: Record<ImageIds, HTMLImageElement>;
|
||||
}
|
||||
|
||||
interface RenderableDataBase {
|
||||
/** 图块的总帧数 */
|
||||
frame: number;
|
||||
/** 对应图块属性的动画帧数,-1表示没有设定 */
|
||||
animate: number;
|
||||
/** 是否是大怪物 */
|
||||
bigImage: boolean;
|
||||
render: [x: number, y: number, width: number, height: number][];
|
||||
}
|
||||
|
||||
export interface RenderableData extends RenderableDataBase {
|
||||
image: SizedCanvasImageSource;
|
||||
autotile: false;
|
||||
}
|
||||
|
||||
export interface AutotileRenderable extends RenderableDataBase {
|
||||
image: Record<string, SizedCanvasImageSource>;
|
||||
autotile: true;
|
||||
bigImage: false;
|
||||
}
|
||||
|
||||
interface TextureCacheEvent extends EmitableEvent {}
|
||||
|
||||
class TextureCache extends EventEmitter<TextureCacheEvent> {
|
||||
@ -49,6 +72,9 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
|
||||
|
||||
idNumberMap!: IdToNumber;
|
||||
|
||||
/** 渲染信息 */
|
||||
renderable: Map<number, RenderableData | AutotileRenderable> = new Map();
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.material = imageMap as Record<ImageMapKeys, HTMLImageElement>;
|
||||
@ -64,6 +90,7 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
|
||||
this.tileset = core.material.images.tilesets;
|
||||
this.autotile = splitAutotiles(this.idNumberMap);
|
||||
this.images = core.material.images.images;
|
||||
this.calRenderable();
|
||||
});
|
||||
}
|
||||
|
||||
@ -78,6 +105,191 @@ class TextureCache extends EventEmitter<TextureCacheEvent> {
|
||||
): TextureRequire[T][K] {
|
||||
return this[type][key];
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算每个图块的可渲染信息
|
||||
*/
|
||||
calRenderable() {
|
||||
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
|
||||
for (const [key, data] of Object.entries(map)) {
|
||||
this.calRenderableByNum(parseInt(key));
|
||||
}
|
||||
}
|
||||
|
||||
calRenderableByNum(
|
||||
num: number
|
||||
): RenderableData | AutotileRenderable | null {
|
||||
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
|
||||
const enemys = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80;
|
||||
const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
|
||||
|
||||
/** 特判空图块与空气墙 */
|
||||
if (num === 0 || num === 17) return null;
|
||||
|
||||
// 额外素材
|
||||
if (num >= 10000) {
|
||||
const offset = core.getTilesetOffset(num);
|
||||
if (!offset) return null;
|
||||
const { image, x, y } = offset;
|
||||
const data: RenderableData = {
|
||||
image: this.tileset[image],
|
||||
frame: 1,
|
||||
render: [[x * 32, y * 32, 32, 32]],
|
||||
animate: 0,
|
||||
autotile: false,
|
||||
bigImage: false
|
||||
};
|
||||
this.renderable.set(num, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
const data = map[num as Exclude<AllNumbers, 0>];
|
||||
// 地狱般的分支if
|
||||
if (data) {
|
||||
let { cls, faceIds, bigImage, id, animate } = data;
|
||||
if (cls === 'enemys' || cls === 'enemy48') {
|
||||
// 怪物需要特殊处理,因为它的大怪物信息不在 maps 里面
|
||||
({ bigImage, faceIds } = enemys[id as EnemyIds]);
|
||||
}
|
||||
if (bigImage) {
|
||||
const image = core.material.images.images[bigImage];
|
||||
if (!image) {
|
||||
logger.warn(
|
||||
10,
|
||||
`Cannot resolve big image of enemy '${id}'.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
let line = 0;
|
||||
if (faceIds) {
|
||||
const arr = ['down', 'left', 'right', 'up'];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (faceIds[arr[i] as Dir] === id) {
|
||||
line = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const totalLines = image.width / image.height >= 2 ? 1 : 4;
|
||||
const w = Math.round(image.width / 4);
|
||||
const h = Math.round(image.height / totalLines);
|
||||
const y = h * line;
|
||||
const data: RenderableData = {
|
||||
image,
|
||||
frame: 4,
|
||||
render: [
|
||||
[0, y, w, h],
|
||||
[w, y, w, h],
|
||||
[w * 2, y, w, h],
|
||||
[w * 3, y, w, h]
|
||||
],
|
||||
animate: (animate ?? 0) - 1,
|
||||
autotile: false,
|
||||
bigImage: true
|
||||
};
|
||||
this.renderable.set(num, data);
|
||||
return data;
|
||||
}
|
||||
// enemy48和npc48都应该视为大怪物
|
||||
if (cls === 'enemy48' || cls === 'npc48') {
|
||||
const img = core.material.images[cls];
|
||||
// @ts-ignore
|
||||
const line = icons[cls][id];
|
||||
const w = 32;
|
||||
const h = 48;
|
||||
const y = h * line;
|
||||
const data: RenderableData = {
|
||||
image: img,
|
||||
frame: 4,
|
||||
render: [
|
||||
[0, y, w, h],
|
||||
[w, y, w, h],
|
||||
[w * 2, y, w, h],
|
||||
[w * 3, y, w, h]
|
||||
],
|
||||
animate: (animate ?? 0) - 1,
|
||||
autotile: false,
|
||||
bigImage: true
|
||||
};
|
||||
this.renderable.set(num, data);
|
||||
return data;
|
||||
}
|
||||
// 自动元件
|
||||
if (cls === 'autotile') {
|
||||
const auto = this.autotile[num as AllNumbersOf<'autotile'>];
|
||||
const cell = 32;
|
||||
const render: [number, number, number, number][] = [];
|
||||
if (auto.frame >= 1) {
|
||||
render.push([0, 0, cell, cell]);
|
||||
}
|
||||
if (auto.frame >= 3) {
|
||||
render.push(
|
||||
[cell, 0, cell, cell],
|
||||
[cell * 2, 0, cell, cell]
|
||||
);
|
||||
}
|
||||
if (auto.frame >= 4) {
|
||||
render.push([cell * 3, 0, cell, cell]);
|
||||
}
|
||||
const data: AutotileRenderable = {
|
||||
image: auto.cache,
|
||||
frame: auto.frame,
|
||||
render,
|
||||
autotile: true,
|
||||
bigImage: false,
|
||||
animate: (animate ?? 0) - 1
|
||||
};
|
||||
this.renderable.set(num, data);
|
||||
return data;
|
||||
} else {
|
||||
const image =
|
||||
core.material.images[
|
||||
cls as Exclude<Cls, 'tileset' | 'autotile'>
|
||||
];
|
||||
const frame = core.getAnimateFrames(cls);
|
||||
const cell = 32;
|
||||
// @ts-ignore
|
||||
const offset = (icons[cls][id] as number) * cell;
|
||||
const render: [number, number, number, number][] = [
|
||||
[0, offset, cell, cell]
|
||||
];
|
||||
if (frame === 2) {
|
||||
render.push([cell, offset, cell, cell]);
|
||||
}
|
||||
if (frame === 4) {
|
||||
render.push(
|
||||
[cell, offset, cell, cell],
|
||||
[cell * 2, offset, cell, cell],
|
||||
[cell * 3, offset, cell, cell]
|
||||
);
|
||||
}
|
||||
const data: RenderableData = {
|
||||
image,
|
||||
frame: frame,
|
||||
render,
|
||||
autotile: false,
|
||||
bigImage: false,
|
||||
animate: (animate ?? 0) - 1
|
||||
};
|
||||
this.renderable.set(num, data);
|
||||
return data;
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
11,
|
||||
`Cannot resolve material ${num}. Material not exists.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取一个图块的渲染信息,自动元件会特别标明autotile属性
|
||||
* @param num 图块数字
|
||||
*/
|
||||
getRenderable(num: number) {
|
||||
return this.renderable.get(num) ?? this.calRenderableByNum(num);
|
||||
}
|
||||
}
|
||||
|
||||
export const texture = new TextureCache();
|
||||
|
@ -26,6 +26,10 @@ export class Container extends RenderItem implements ICanvasCachedRenderItem {
|
||||
camera: Camera
|
||||
): void {
|
||||
this.emit('beforeRender');
|
||||
if (this.needUpdate) {
|
||||
this.cache(this.writing);
|
||||
this.needUpdate = false;
|
||||
}
|
||||
withCacheRender(this, canvas, ctx, camera, c => {
|
||||
this.sortedChildren.forEach(v => {
|
||||
if (!v.antiAliasing) {
|
||||
|
@ -77,7 +77,7 @@ interface IRenderConfig {
|
||||
setHD(hd: boolean): void;
|
||||
|
||||
/**
|
||||
* 设置当前渲染原始是否启用抗锯齿
|
||||
* 设置当前渲染元素是否启用抗锯齿
|
||||
* @param anti 是否抗锯齿
|
||||
*/
|
||||
setAntiAliasing(anti: boolean): void;
|
||||
@ -135,7 +135,7 @@ export abstract class RenderItem
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.using = '@default';
|
||||
// this.using = '@default';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,21 +194,7 @@ export abstract class RenderItem
|
||||
update(item?: RenderItem): void {
|
||||
if (this.needUpdate) return;
|
||||
this.needUpdate = true;
|
||||
requestAnimationFrame(() => {
|
||||
this.needUpdate = false;
|
||||
if (!this.parent) return;
|
||||
this.cache(this.writing);
|
||||
this.refresh(item);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 立刻更新这个组件,不延迟到下一个tick
|
||||
*/
|
||||
protected refresh(item?: RenderItem) {
|
||||
this.emit('beforeUpdate', item);
|
||||
this.parent?.refresh(item);
|
||||
this.emit('afterUpdate', item);
|
||||
this.parent?.update(item);
|
||||
}
|
||||
|
||||
setHD(hd: boolean): void {
|
||||
@ -223,7 +209,7 @@ export abstract class RenderItem
|
||||
|
||||
setZIndex(zIndex: number) {
|
||||
this.zIndex = zIndex;
|
||||
(this.parent as Container).sortChildren?.();
|
||||
(this.parent as Container)?.sortChildren?.();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,7 @@ import { Camera } from '../camera';
|
||||
import { TimingFn } from 'mutate-animate';
|
||||
import { IRenderDestroyable, RenderItem } from '../item';
|
||||
import { logger } from '@/core/common/logger';
|
||||
import { texture } from '../cache';
|
||||
import { SizedCanvasImageSource } from './misc';
|
||||
import { AutotileRenderable, RenderableData, texture } from '../cache';
|
||||
import { glMatrix } from 'gl-matrix';
|
||||
|
||||
interface LayerCacheItem {
|
||||
@ -14,24 +13,12 @@ interface LayerCacheItem {
|
||||
canvas: HTMLCanvasElement;
|
||||
}
|
||||
|
||||
interface LayerRenderableData {
|
||||
image: SizedCanvasImageSource;
|
||||
frame: number;
|
||||
render: [x: number, y: number, width: number, height: number][];
|
||||
}
|
||||
|
||||
interface LayerMovingRenderable extends LayerRenderableData {
|
||||
interface LayerMovingRenderable extends RenderableData {
|
||||
zIndex: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface BigImageData {
|
||||
image: HTMLImageElement;
|
||||
line: number;
|
||||
totalLines: number;
|
||||
}
|
||||
|
||||
interface NeedRenderData {
|
||||
/** 需要渲染的地图内容 */
|
||||
res: Set<number>;
|
||||
@ -82,7 +69,7 @@ interface MovingBlock {
|
||||
/** 目标纵坐标 */
|
||||
y: number;
|
||||
/** 渲染信息 */
|
||||
render: LayerRenderableData;
|
||||
render: RenderableData | AutotileRenderable;
|
||||
/** 当前的纵深 */
|
||||
nowZ: number;
|
||||
}
|
||||
@ -124,10 +111,6 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
layer?: FloorLayer;
|
||||
/** 渲染数据 */
|
||||
renderData: number[] = [];
|
||||
/** 可以直接被渲染的内容 */
|
||||
renderable: Map<number, LayerRenderableData> = new Map();
|
||||
/** 移动层中可以直接被渲染的内容 */
|
||||
movingRenderable: LayerMovingRenderable[] = [];
|
||||
/** 自动元件的连接信息,键表示图块在渲染数据中的索引,值表示连接信息,是个8位二进制 */
|
||||
autotiles: Record<number, number> = {};
|
||||
/** 楼层宽度 */
|
||||
@ -136,8 +119,6 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
mapHeight: number = 0;
|
||||
/** 每个图块的大小 */
|
||||
cellSize: number = 32;
|
||||
/** moving层的缓存信息,从低位到高位依次是第1帧至第4帧 */
|
||||
movingCached: number = 0b0000;
|
||||
|
||||
/** 背景图块 */
|
||||
background: AllNumbers = 0;
|
||||
@ -152,28 +133,33 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
restWidth: 0
|
||||
};
|
||||
blockSize: number = core._WIDTH_;
|
||||
|
||||
/** 正在移动的图块 */
|
||||
moving: MovingBlock[] = [];
|
||||
/** 大怪物(大图块)信息,键是图块在渲染数据中的索引,值是大怪物所用图片 */
|
||||
bigImage: Map<number, BigImageData> = new Map();
|
||||
/** 大怪物渲染信息 */
|
||||
bigImages: Map<number, LayerMovingRenderable> = new Map();
|
||||
/** 移动层的渲染信息 */
|
||||
movingRenderable: LayerMovingRenderable[] = [];
|
||||
/** 下一此渲染时是否需要更新移动层的渲染信息 */
|
||||
needUpdateMoving: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super('absolute');
|
||||
|
||||
this.setHD(false);
|
||||
// this.setHD(false);
|
||||
this.setAntiAliasing(false);
|
||||
this.size(core._PX_, core._PY_);
|
||||
|
||||
this.staticMap.setHD(false);
|
||||
this.staticMap.setAntiAliasing(false);
|
||||
// this.staticMap.setAntiAliasing(false);
|
||||
this.staticMap.withGameScale(false);
|
||||
this.staticMap.size(core._PX_, core._PY_);
|
||||
this.movingMap.setHD(false);
|
||||
this.movingMap.setAntiAliasing(false);
|
||||
// this.movingMap.setAntiAliasing(false);
|
||||
this.movingMap.withGameScale(false);
|
||||
this.movingMap.size(core._PX_, core._PY_);
|
||||
this.backMap.setHD(false);
|
||||
this.backMap.setAntiAliasing(false);
|
||||
// this.backMap.setAntiAliasing(false);
|
||||
this.backMap.withGameScale(false);
|
||||
this.backMap.size(core._PX_, core._PY_);
|
||||
this.main.setAntiAliasing(false);
|
||||
@ -211,7 +197,7 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
generateBackground() {
|
||||
const num = this.background;
|
||||
|
||||
const data = this.getRenderableByNum(num);
|
||||
const data = texture.getRenderable(num);
|
||||
this.backImage = [];
|
||||
if (!data) return;
|
||||
|
||||
@ -231,7 +217,7 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
temp.withGameScale(false);
|
||||
temp.size(w, h);
|
||||
|
||||
const img = data.image;
|
||||
const img = data.autotile ? data.image[0b11111111] : data.image;
|
||||
tempCtx.drawImage(img, sx, sy, w, h, 0, 0, w, h);
|
||||
const pattern = ctx.createPattern(temp.canvas, 'repeat');
|
||||
if (!pattern) continue;
|
||||
@ -256,8 +242,6 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
y: number = 0,
|
||||
calAutotile: boolean = true
|
||||
) {
|
||||
console.trace();
|
||||
|
||||
if (data.length % width !== 0) {
|
||||
logger.warn(
|
||||
8,
|
||||
@ -286,251 +270,38 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
}
|
||||
}
|
||||
if (calAutotile) this.calAutotiles(x, y, width, height);
|
||||
this.updateBigImages(x, y, width, height);
|
||||
this.updateRenderableData(x, y, width, height);
|
||||
// this.updateBigImages(x, y, width, height);
|
||||
// this.updateRenderableData(x, y, width, height);
|
||||
this.updateBlocks(x, y, width, height);
|
||||
this.update(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新给定区域内的大怪物信息
|
||||
* 更新大怪物的渲染信息
|
||||
*/
|
||||
updateBigImages(x: number, y: number, width: number, height: number) {
|
||||
const ex = Math.min(x + width, this.mapWidth);
|
||||
const ey = Math.min(y + height, this.mapHeight);
|
||||
const size = this.blockSize;
|
||||
const images = this.bigImage;
|
||||
const ex = x + width;
|
||||
const ey = y + height;
|
||||
const w = this.mapWidth;
|
||||
const data = this.renderData;
|
||||
const enemys = enemys_fcae963b_31c9_42b4_b48c_bb48d09f3f80;
|
||||
const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
|
||||
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
|
||||
|
||||
for (let nx = Math.max(x, 0); nx < ex; nx++) {
|
||||
for (let ny = Math.max(y, 0); ny < ey; ny++) {
|
||||
const index = ny * size + nx;
|
||||
images.delete(index);
|
||||
for (let nx = x; nx < ex; nx++) {
|
||||
for (let ny = y; ny < ey; ny++) {
|
||||
const index = ny * w + nx;
|
||||
this.bigImages.delete(index);
|
||||
const num = data[index];
|
||||
|
||||
// 如果不存在图块或图块是空气墙,跳过
|
||||
if (num === 0 || num === 17 || num >= 10000) continue;
|
||||
|
||||
let { cls, id, bigImage, faceIds } =
|
||||
map[num as Exclude<AllNumbers, 0>];
|
||||
if (cls === 'enemys' || cls === 'enemy48') {
|
||||
// 怪物需要特殊处理,因为它的大怪物信息不在 maps 里面
|
||||
({ bigImage, faceIds } = enemys[id as EnemyIds]);
|
||||
}
|
||||
if (bigImage) {
|
||||
const image = core.material.images.images[bigImage];
|
||||
if (!image) {
|
||||
logger.warn(
|
||||
10,
|
||||
`Cannot resolve big image of enemy '${id}'.`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let line = 0;
|
||||
if (faceIds) {
|
||||
const arr = ['down', 'left', 'right', 'up'];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (faceIds[arr[i] as Dir] === id) {
|
||||
line = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const totalLines = image.width / image.height >= 2 ? 1 : 4;
|
||||
images.set(index, {
|
||||
image,
|
||||
line,
|
||||
totalLines
|
||||
});
|
||||
}
|
||||
if (cls === 'enemy48' || cls === 'npc48') {
|
||||
// 32 * 48 视为大怪物
|
||||
const img = core.material.images[cls];
|
||||
const totalLines = Math.round(img.height / 48);
|
||||
// @ts-ignore
|
||||
const line = icons[cls][id];
|
||||
images.set(index, {
|
||||
image: img,
|
||||
line,
|
||||
totalLines
|
||||
});
|
||||
}
|
||||
const renderable = texture.getRenderable(num);
|
||||
if (!renderable || !renderable.bigImage) continue;
|
||||
this.bigImages.set(index, {
|
||||
...renderable,
|
||||
x: nx,
|
||||
y: ny,
|
||||
zIndex: ny
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据图块数字获取渲染信息
|
||||
* @param num 图块数字
|
||||
*/
|
||||
getRenderableByNum(num: number): LayerRenderableData | null {
|
||||
const cell = this.cellSize;
|
||||
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
|
||||
const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
|
||||
const auto = texture.autotile;
|
||||
|
||||
if (num >= 10000) {
|
||||
// 额外素材
|
||||
const offset = core.getTilesetOffset(num);
|
||||
if (!offset) return null;
|
||||
const { image, x, y } = offset;
|
||||
return {
|
||||
image: core.material.images.tilesets[image],
|
||||
frame: 1,
|
||||
render: [[x * cell, y * cell, cell, cell]]
|
||||
};
|
||||
} else {
|
||||
if (num === 0 || num === 17) return null;
|
||||
const { cls, id } = map[num as Exclude<AllNumbers, 0>];
|
||||
// 普通素材
|
||||
if (cls !== 'autotile') {
|
||||
const image =
|
||||
core.material.images[
|
||||
cls as Exclude<Cls, 'tileset' | 'autotile'>
|
||||
];
|
||||
const frame = core.getAnimateFrames(cls);
|
||||
// @ts-ignore
|
||||
const offset = (icons[cls][id] as number) * cell;
|
||||
const render: [number, number, number, number][] = [
|
||||
[0, offset, cell, cell]
|
||||
];
|
||||
if (frame === 2) {
|
||||
render.push([cell, offset, cell, cell]);
|
||||
}
|
||||
if (frame === 4) {
|
||||
render.push(
|
||||
[cell, offset, cell, cell],
|
||||
[cell * 2, offset, cell, cell],
|
||||
[cell * 3, offset, cell, cell]
|
||||
);
|
||||
}
|
||||
return {
|
||||
image,
|
||||
frame,
|
||||
render
|
||||
};
|
||||
} else {
|
||||
// 自动元件
|
||||
const tile = auto[num as AllNumbersOf<'autotile'>];
|
||||
const image = tile.cache[0b11111111];
|
||||
const frame = tile.frame;
|
||||
const render: [number, number, number, number][] = [
|
||||
[0, 0, cell, cell]
|
||||
];
|
||||
if (frame === 4) {
|
||||
render.push(
|
||||
[cell, 0, cell, cell],
|
||||
[cell * 2, 0, cell, cell],
|
||||
[cell * 3, 0, cell, cell]
|
||||
);
|
||||
}
|
||||
return {
|
||||
image,
|
||||
frame,
|
||||
render
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引及横坐标获取对应的位置图块的渲染信息
|
||||
* @param x 横坐标
|
||||
* @param index 索引
|
||||
*/
|
||||
getRenderableData(
|
||||
x: number,
|
||||
_y: number,
|
||||
index: number
|
||||
): LayerRenderableData | LayerMovingRenderable | null {
|
||||
const data = this.renderData;
|
||||
const cell = this.cellSize;
|
||||
const map = maps_90f36752_8815_4be8_b32b_d7fad1d0542e;
|
||||
const icons = icons_4665ee12_3a1f_44a4_bea3_0fccba634dc1;
|
||||
const auto = texture.autotile;
|
||||
|
||||
const bigImage = this.bigImage.get(index);
|
||||
if (bigImage) {
|
||||
// 对于大怪物
|
||||
const img = bigImage.image;
|
||||
const w = Math.round(img.width / 4);
|
||||
const h = Math.round(img.height / bigImage.totalLines);
|
||||
const y = h * bigImage.line;
|
||||
return {
|
||||
image: bigImage.image,
|
||||
frame: 4,
|
||||
render: [
|
||||
[0, y, w, h],
|
||||
[w, y, w, h],
|
||||
[w * 2, y, w, h],
|
||||
[w * 3, y, w, h]
|
||||
],
|
||||
x: x,
|
||||
y: y,
|
||||
zIndex: y
|
||||
};
|
||||
} else {
|
||||
// 对于普通图块
|
||||
const num = data[index];
|
||||
if (num >= 10000) {
|
||||
// 额外素材
|
||||
return this.getRenderableByNum(num);
|
||||
} else {
|
||||
if (num === 0 || num === 17) return null;
|
||||
const { cls } = map[num as Exclude<AllNumbers, 0>];
|
||||
// 普通素材
|
||||
if (cls !== 'autotile') {
|
||||
return this.getRenderableByNum(num);
|
||||
} else {
|
||||
// 自动元件
|
||||
const tile = auto[num as AllNumbersOf<'autotile'>];
|
||||
const link = this.autotiles[index];
|
||||
const image = tile.cache[link];
|
||||
const frame = tile.frame;
|
||||
const render: [number, number, number, number][] = [
|
||||
[0, 0, cell, cell]
|
||||
];
|
||||
if (frame === 4) {
|
||||
render.push(
|
||||
[cell, 0, cell, cell],
|
||||
[cell * 2, 0, cell, cell],
|
||||
[cell * 3, 0, cell, cell]
|
||||
);
|
||||
}
|
||||
return {
|
||||
image,
|
||||
frame,
|
||||
render
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新给定区域内的绘制信息
|
||||
*/
|
||||
updateRenderableData(x: number, y: number, width: number, height: number) {
|
||||
const ex = Math.min(x + width, this.mapWidth);
|
||||
const ey = Math.min(y + height, this.mapHeight);
|
||||
const size = this.blockSize;
|
||||
|
||||
for (let nx = Math.max(x, 0); nx < ex; nx++) {
|
||||
for (let ny = Math.max(y, 0); ny < ey; ny++) {
|
||||
const index = ny * size + nx;
|
||||
const bigImage = this.bigImage.get(index);
|
||||
const data = this.getRenderableData(nx, ny, index);
|
||||
if (!data) continue;
|
||||
if (bigImage) {
|
||||
this.movingRenderable.push(data as LayerMovingRenderable);
|
||||
} else {
|
||||
this.renderable.set(index, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.needUpdateMoving = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -590,12 +361,11 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
|
||||
for (let nx = x; nx < ex; nx++) {
|
||||
for (let ny = y; ny < ey; ny++) {
|
||||
if (nx > w || ny > w) continue;
|
||||
const index = nx + ny * h;
|
||||
if (nx > w || ny > h) continue;
|
||||
const index = nx + ny * w;
|
||||
const num = data[index];
|
||||
// 特判空气墙与空图块
|
||||
if (num === 17 || num >= 10000 || num <= 0) continue;
|
||||
console.log(this);
|
||||
if (num === 0 || num === 17 || num >= 10000) continue;
|
||||
|
||||
const info = map[num as Exclude<AllNumbers, 0>];
|
||||
const { cls } = info;
|
||||
@ -683,8 +453,8 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
const size = this.blockSize;
|
||||
|
||||
this.blockData = {
|
||||
width: Math.floor(this.mapWidth / size),
|
||||
height: Math.floor(this.mapHeight / size),
|
||||
width: Math.ceil(this.mapWidth / size),
|
||||
height: Math.ceil(this.mapHeight / size),
|
||||
restWidth: this.mapWidth % size,
|
||||
restHeight: this.mapHeight % size
|
||||
};
|
||||
@ -808,21 +578,31 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
* @param camera 摄像机
|
||||
*/
|
||||
calNeedRender(camera: Camera): NeedRenderData {
|
||||
const w = core._WIDTH_;
|
||||
const h = core._HEIGHT_;
|
||||
const size = this.blockSize;
|
||||
const { width } = this.blockData;
|
||||
const cell = this.cellSize;
|
||||
const w = (core._WIDTH_ * cell) / 2;
|
||||
const h = (core._HEIGHT_ * cell) / 2;
|
||||
const size = this.blockSize;
|
||||
|
||||
const [x1, y1] = Camera.transformed(camera, 0, 0);
|
||||
const [x2, y2] = Camera.transformed(camera, w * cell, 0);
|
||||
const [x3, y3] = Camera.transformed(camera, w * cell, h * cell);
|
||||
const [x4, y4] = Camera.transformed(camera, 0, h * cell);
|
||||
// -1是因为宽度是core._PX_,从0开始的话,末尾索引就是core._PX_ - 1
|
||||
const [x1, y1] = Camera.untransformed(camera, -w, -h);
|
||||
const [x2, y2] = Camera.untransformed(camera, w - 1, -h);
|
||||
const [x3, y3] = Camera.untransformed(camera, w - 1, h - 1);
|
||||
const [x4, y4] = Camera.untransformed(camera, -w, h - 1);
|
||||
|
||||
const res: Set<number> = new Set();
|
||||
/** 一个纵坐标对应的所有横坐标,用于填充 */
|
||||
const xyMap: Map<number, number[]> = new Map();
|
||||
|
||||
const pushXY = (x: number, y: number) => {
|
||||
let arr = xyMap.get(y);
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
xyMap.set(y, arr);
|
||||
}
|
||||
arr.push(x);
|
||||
return arr;
|
||||
};
|
||||
|
||||
[
|
||||
[x1, y1, x2, y2],
|
||||
[x2, y2, x3, y3],
|
||||
@ -837,43 +617,26 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
const dy = ty - fy;
|
||||
const k = dy / dx;
|
||||
|
||||
// console.log(fx, fy, tx, ty);
|
||||
|
||||
// 斜率无限的时候,竖直
|
||||
if (!isFinite(k)) {
|
||||
const min = k < 0 ? ty : fy;
|
||||
const max = k < 0 ? fy : ty;
|
||||
const [x, y] = this.getBlockXYByLoc(fx, min);
|
||||
|
||||
// 在地图左侧或右侧时,将每个纵坐标对应的横坐标填充为0
|
||||
|
||||
const p = x < 0 ? 0 : x >= width ? width - 1 : x;
|
||||
const [, ey] = this.getBlockXYByLoc(fx, max);
|
||||
for (let i = y; i <= ey; i++) {
|
||||
let arr = xyMap.get(i);
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
xyMap.set(i, arr);
|
||||
}
|
||||
arr.push(p);
|
||||
pushXY(x, i);
|
||||
}
|
||||
// console.log(y, ey, p);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const [fbx, fby] = this.getBlockXYByLoc(fx, fy);
|
||||
|
||||
// 当斜率为0时
|
||||
if (glMatrix.equals(k, 0)) {
|
||||
const [ex] = this.getBlockXYByLoc(tx, fy);
|
||||
let arr = xyMap.get(fby);
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
xyMap.set(fby, arr);
|
||||
}
|
||||
arr.push(fbx, ex);
|
||||
// console.log(fbx, ex);
|
||||
|
||||
pushXY(fby, fbx).push(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -881,54 +644,52 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
if (Math.abs(k) >= 1) {
|
||||
// 斜率大于一,y方向递增
|
||||
const d = Math.sign(dy) * size;
|
||||
const f = dx > 0 ? fby * size : (fby + 1) * size;
|
||||
const f = fby * size;
|
||||
const dir = dy > 0;
|
||||
|
||||
const ex = Math.floor(tx / size);
|
||||
const ey = Math.floor(ty / size);
|
||||
pushXY(ex, ey);
|
||||
|
||||
let now = f;
|
||||
let last = fbx;
|
||||
let ny = fby;
|
||||
do {
|
||||
const bx = Math.floor(fx + (now - fy) / k);
|
||||
let arr = xyMap.get(ny);
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
xyMap.set(ny, arr);
|
||||
}
|
||||
const bx = Math.floor((fx + (now - fy) / k) / size);
|
||||
pushXY(bx, ny);
|
||||
if (bx !== last) {
|
||||
arr.push(last);
|
||||
if (dir) pushXY(bx, ny - Math.sign(dy));
|
||||
else pushXY(last, ny);
|
||||
}
|
||||
arr.push(bx);
|
||||
|
||||
last = bx;
|
||||
ny++;
|
||||
} while (dir ? (now += d) < ty : (now += d) > ty);
|
||||
ny += Math.sign(dy);
|
||||
now += d;
|
||||
} while (dir ? ny <= ey : ny >= ey);
|
||||
} else {
|
||||
// 斜率小于一,x方向递增
|
||||
const d = Math.sign(dx) * size;
|
||||
const f = dx > 0 ? fbx * size : (fbx + 1) * size;
|
||||
const f = fbx * size;
|
||||
const dir = dx > 0;
|
||||
|
||||
const ex = Math.floor(tx / size);
|
||||
const ey = Math.floor(ty / size);
|
||||
pushXY(ex, ey);
|
||||
|
||||
let now = f;
|
||||
let last = fby;
|
||||
let nx = fbx;
|
||||
do {
|
||||
const by = Math.floor(fy + k * (now - fx));
|
||||
|
||||
const by = Math.floor((fy + k * (now - fx)) / size);
|
||||
if (by !== last) {
|
||||
let arr = xyMap.get(last);
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
xyMap.set(last, arr);
|
||||
}
|
||||
arr.push(nx);
|
||||
if (dir) pushXY(nx - Math.sign(dx), by);
|
||||
else pushXY(nx, last);
|
||||
}
|
||||
let arr = xyMap.get(nx);
|
||||
if (!arr) {
|
||||
arr = [];
|
||||
xyMap.set(by, arr);
|
||||
}
|
||||
arr.push(nx);
|
||||
nx++;
|
||||
} while (dir ? (now += d) < tx : (now += d) > tx);
|
||||
pushXY(nx, by);
|
||||
last = by;
|
||||
nx += Math.sign(dx);
|
||||
now += d;
|
||||
} while (dir ? nx <= ex : nx >= ex);
|
||||
}
|
||||
});
|
||||
|
||||
@ -939,7 +700,8 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
if (x.length === 1) {
|
||||
const index = y * bw + x[0];
|
||||
|
||||
back.push([x[0], y]);
|
||||
if (!back.some(v => v[0] === x[0] && v[1] === y))
|
||||
back.push([x[0], y]);
|
||||
if (index < 0 || index >= bw * bh) return;
|
||||
res.add(index);
|
||||
}
|
||||
@ -949,17 +711,44 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
for (let i = min; i <= max; i++) {
|
||||
const index = y * bw + i;
|
||||
|
||||
back.push([i, y]);
|
||||
if (!back.some(v => v[0] === i && v[1] === y))
|
||||
back.push([i, y]);
|
||||
if (index < 0 || index >= bw * bh) continue;
|
||||
res.add(index);
|
||||
}
|
||||
});
|
||||
|
||||
// console.log([...res], xyMap);
|
||||
|
||||
return { res, back };
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新移动层的渲染信息
|
||||
*/
|
||||
updateMovingRenderable() {
|
||||
this.movingRenderable = [];
|
||||
this.movingRenderable.push(...this.bigImages.values());
|
||||
this.moving.forEach(v => {
|
||||
if (!v.render.autotile) {
|
||||
this.movingRenderable.push({
|
||||
...v.render,
|
||||
x: v.x,
|
||||
y: v.y,
|
||||
zIndex: v.nowZ
|
||||
});
|
||||
} else {
|
||||
this.movingRenderable.push({
|
||||
...v.render,
|
||||
x: v.x,
|
||||
y: v.y,
|
||||
zIndex: v.nowZ,
|
||||
image: v.render.image[0b00000000],
|
||||
autotile: false
|
||||
});
|
||||
}
|
||||
});
|
||||
this.movingRenderable.sort((a, b) => a.zIndex - b.zIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染当前地图
|
||||
*/
|
||||
@ -968,6 +757,8 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
this.movingMap.clear();
|
||||
this.backMap.clear();
|
||||
|
||||
if (this.needUpdateMoving) this.updateMovingRenderable();
|
||||
|
||||
this.renderBack(camera, need);
|
||||
this.renderStatic(camera, need);
|
||||
this.renderMoving(camera);
|
||||
@ -981,10 +772,9 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
protected renderBack(camera: Camera, need: NeedRenderData) {
|
||||
const cell = this.cellSize;
|
||||
const frame = (RenderItem.animatedFrame % 4) + 1;
|
||||
const { width } = this.blockData;
|
||||
const blockSize = this.blockSize;
|
||||
const { back } = need;
|
||||
const { ctx, canvas } = this.backMap;
|
||||
const { ctx } = this.backMap;
|
||||
|
||||
const mat = camera.mat;
|
||||
const a = mat[0];
|
||||
@ -1020,16 +810,14 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
*/
|
||||
protected renderStatic(camera: Camera, need: NeedRenderData) {
|
||||
const cell = this.cellSize;
|
||||
const renderable = this.renderable;
|
||||
const frame = (RenderItem.animatedFrame % 4) + 1;
|
||||
const { width } = this.blockData;
|
||||
const blockSize = this.blockSize;
|
||||
const { ctx, canvas } = this.staticMap;
|
||||
const { ctx } = this.staticMap;
|
||||
|
||||
ctx.save();
|
||||
|
||||
const { res: render } = need;
|
||||
// console.log(render);
|
||||
const mat = camera.mat;
|
||||
const a = mat[0];
|
||||
const b = mat[1];
|
||||
@ -1061,8 +849,8 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
return;
|
||||
}
|
||||
|
||||
const ex = sx + blockSize;
|
||||
const ey = sy + blockSize;
|
||||
const ex = Math.min(sx + blockSize, this.mapWidth);
|
||||
const ey = Math.min(sy + blockSize, this.mapHeight);
|
||||
|
||||
const temp = new MotaOffscreenCanvas2D();
|
||||
temp.setAntiAliasing(false);
|
||||
@ -1074,17 +862,28 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
for (let nx = sx; nx < ex; nx++) {
|
||||
for (let ny = sy; ny < ey; ny++) {
|
||||
const blockIndex = nx + ny * this.mapWidth;
|
||||
const data = renderable.get(blockIndex);
|
||||
if (!data) continue;
|
||||
|
||||
const num = this.renderData[blockIndex];
|
||||
if (num === 0 || num === 17) continue;
|
||||
const data = texture.getRenderable(num);
|
||||
if (!data || data.bigImage) continue;
|
||||
const f = frame % data.frame;
|
||||
const i = frame === 4 && data.frame === 3 ? 1 : f;
|
||||
const [sx, sy, w, h] = data.render[i];
|
||||
const px = nx * cell;
|
||||
const py = ny * cell;
|
||||
const image = data.image;
|
||||
|
||||
temp.ctx.drawImage(image, sx, sy, w, h, px, py, w, h);
|
||||
const i =
|
||||
data.animate === -1
|
||||
? frame === 4 && data.frame === 3
|
||||
? 1
|
||||
: f
|
||||
: data.animate;
|
||||
const [isx, isy, w, h] = data.render[i];
|
||||
const px = (nx - sx) * cell;
|
||||
const py = (ny - sy) * cell;
|
||||
const { image, autotile } = data;
|
||||
if (!autotile) {
|
||||
temp.ctx.drawImage(image, isx, isy, w, h, px, py, w, h);
|
||||
} else {
|
||||
const link = this.autotiles[blockIndex];
|
||||
const i = image[link];
|
||||
temp.ctx.drawImage(i, isx, isy, w, h, px, py, w, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.drawImage(
|
||||
@ -1126,10 +925,6 @@ export class Layer extends Container implements IRenderDestroyable {
|
||||
const r =
|
||||
Math.max(a, b, c, d) ** 2 * Math.max(core._PX_, core._PY_) * 2;
|
||||
|
||||
this.movingRenderable.sort((a, b) => {
|
||||
return a.zIndex - b.zIndex;
|
||||
});
|
||||
|
||||
this.movingRenderable.forEach(v => {
|
||||
const { x, y, image, frame: blockFrame, render } = v;
|
||||
const f = frame % 4;
|
||||
|
@ -149,11 +149,11 @@ Mota.require('var', 'hook').once('reset', () => {
|
||||
const camera = render.camera;
|
||||
render.mount();
|
||||
|
||||
layer.zIndex = 2;
|
||||
bgLayer.zIndex = 1;
|
||||
layer.setZIndex(2);
|
||||
bgLayer.setZIndex(1);
|
||||
render.appendChild([layer, bgLayer]);
|
||||
layer.bindThis('event', true);
|
||||
bgLayer.bindThis('bg', true);
|
||||
layer.bindThis('event');
|
||||
bgLayer.bindThis('bg');
|
||||
bgLayer.setBackground(650);
|
||||
|
||||
const ani = new Animation();
|
||||
@ -161,17 +161,35 @@ Mota.require('var', 'hook').once('reset', () => {
|
||||
ani.ticker.add(() => {
|
||||
camera.reset();
|
||||
camera.rotate((ani.angle / 180) * Math.PI);
|
||||
camera.move(240, 240);
|
||||
camera.move(ani.x, ani.y);
|
||||
camera.scale(ani.size);
|
||||
render.update(render);
|
||||
});
|
||||
|
||||
camera.rotate(Math.PI * 1.23);
|
||||
camera.move(230, 380);
|
||||
camera.scale(0.7);
|
||||
render.update();
|
||||
|
||||
// sleep(2000).then(() => {
|
||||
// render.update();
|
||||
// });
|
||||
|
||||
sleep(1000).then(() => {
|
||||
ani.mode(hyper('sin', 'out')).time(100).absolute().rotate(30);
|
||||
ani.mode(hyper('sin', 'out'))
|
||||
.time(100)
|
||||
.absolute()
|
||||
.rotate(30)
|
||||
.move(240, 240);
|
||||
sleep(100).then(() => {
|
||||
ani.time(3000).rotate(0);
|
||||
});
|
||||
sleep(3100).then(() => {
|
||||
ani.time(5000).mode(hyper('tan', 'in-out')).rotate(3600);
|
||||
ani.time(5000)
|
||||
.mode(hyper('sin', 'in-out'))
|
||||
.rotate(360)
|
||||
.move(200, 480)
|
||||
.scale(0.5);
|
||||
});
|
||||
// ani.mode(shake2(5, hyper('sin', 'in-out')), true)
|
||||
// .time(5000)
|
||||
|
@ -25,6 +25,10 @@ export class Sprite extends RenderItem implements ICanvasCachedRenderItem {
|
||||
camera: Camera
|
||||
): void {
|
||||
this.emit('beforeRender');
|
||||
if (this.needUpdate) {
|
||||
this.cache(this.writing);
|
||||
this.needUpdate = false;
|
||||
}
|
||||
withCacheRender(this, canvas, ctx, camera, canvas => {
|
||||
this.renderFn(canvas, camera);
|
||||
});
|
||||
|
2
src/types/core.d.ts
vendored
2
src/types/core.d.ts
vendored
@ -1419,6 +1419,8 @@ interface MapDataOf<T extends keyof NumberToId> {
|
||||
bigImage?: ImageIds;
|
||||
|
||||
faceIds?: Record<Dir, AllIds>;
|
||||
|
||||
animate?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user