canvasengine 1.3.0 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +51 -17
- package/src/components/Canvas.ts +134 -0
- package/src/components/Container.ts +46 -0
- package/src/components/DisplayObject.ts +458 -0
- package/src/components/DrawMap/index.ts +65 -0
- package/src/components/Graphic.ts +147 -0
- package/src/components/NineSliceSprite.ts +46 -0
- package/src/components/ParticleEmitter.ts +39 -0
- package/src/components/Scene.ts +6 -0
- package/src/components/Sprite.ts +493 -0
- package/src/components/Text.ts +145 -0
- package/src/components/Tilemap/Tile.ts +79 -0
- package/src/components/Tilemap/TileGroup.ts +207 -0
- package/src/components/Tilemap/TileLayer.ts +163 -0
- package/src/components/Tilemap/TileSet.ts +41 -0
- package/src/components/Tilemap/index.ts +80 -0
- package/src/components/TilingSprite.ts +39 -0
- package/src/components/Viewport.ts +159 -0
- package/src/components/index.ts +13 -0
- package/src/components/types/DisplayObject.ts +69 -0
- package/src/components/types/MouseEvent.ts +3 -0
- package/src/components/types/Spritesheet.ts +389 -0
- package/src/components/types/index.ts +4 -0
- package/src/directives/Drag.ts +84 -0
- package/src/directives/KeyboardControls.ts +922 -0
- package/src/directives/Scheduler.ts +101 -0
- package/src/directives/Sound.ts +91 -0
- package/src/directives/Transition.ts +45 -0
- package/src/directives/ViewportCull.ts +40 -0
- package/src/directives/ViewportFollow.ts +26 -0
- package/src/directives/index.ts +7 -0
- package/src/engine/animation.ts +113 -0
- package/src/engine/bootstrap.ts +19 -0
- package/src/engine/directive.ts +23 -0
- package/src/engine/reactive.ts +379 -0
- package/src/engine/signal.ts +138 -0
- package/src/engine/trigger.ts +40 -0
- package/src/engine/utils.ts +135 -0
- package/src/hooks/addContext.ts +6 -0
- package/src/hooks/useProps.ts +155 -0
- package/src/hooks/useRef.ts +21 -0
- package/src/index.ts +13 -0
- package/src/utils/Ease.ts +33 -0
- package/src/utils/RadialGradient.ts +86 -0
- package/.gitattributes +0 -22
- package/.npmignore +0 -163
- package/canvasengine-1.3.0.all.min.js +0 -21
- package/canvasengine.js +0 -5802
- package/core/DB.js +0 -24
- package/core/ModelServer.js +0 -348
- package/core/Users.js +0 -190
- package/core/engine-common.js +0 -952
- package/doc/cocoonjs.md +0 -36
- package/doc/doc-lang.yml +0 -43
- package/doc/doc-router.yml +0 -14
- package/doc/doc-tuto.yml +0 -9
- package/doc/doc.yml +0 -39
- package/doc/element.md +0 -37
- package/doc/entity.md +0 -90
- package/doc/extend.md +0 -47
- package/doc/get_started.md +0 -19
- package/doc/images/entity.png +0 -0
- package/doc/multitouch.md +0 -58
- package/doc/nodejs.md +0 -142
- package/doc/scene.md +0 -44
- package/doc/text.md +0 -156
- package/examples/server/client.html +0 -31
- package/examples/server/server.js +0 -16
- package/examples/tiled_server/client.html +0 -52
- package/examples/tiled_server/images/tiles_spritesheet.png +0 -0
- package/examples/tiled_server/server/map.json +0 -50
- package/examples/tiled_server/server/map.tmx +0 -16
- package/examples/tiled_server/server/server.js +0 -16
- package/extends/Animation.js +0 -910
- package/extends/Effect.js +0 -252
- package/extends/Gleed2d.js +0 -252
- package/extends/Hit.js +0 -1509
- package/extends/Input.js +0 -699
- package/extends/Marshal.js +0 -716
- package/extends/Scrolling.js +0 -388
- package/extends/Soundmanager2.js +0 -5466
- package/extends/Spritesheet.js +0 -196
- package/extends/Text.js +0 -366
- package/extends/Tiled.js +0 -403
- package/extends/Window.js +0 -575
- package/extends/gamepad.js +0 -397
- package/extends/socket.io.min.js +0 -2
- package/extends/swf/soundmanager2.swf +0 -0
- package/extends/swf/soundmanager2_debug.swf +0 -0
- package/extends/swf/soundmanager2_flash9.swf +0 -0
- package/extends/swf/soundmanager2_flash9_debug.swf +0 -0
- package/extends/swf/soundmanager2_flash_xdomain.zip +0 -0
- package/extends/workers/transition.js +0 -43
- package/index.js +0 -46
- package/license.txt +0 -19
- package/readme.md +0 -483
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { computed, effect, isSignal, Signal, WritableSignal } from "@signe/reactive";
|
|
2
|
+
import {
|
|
3
|
+
Assets,
|
|
4
|
+
Container,
|
|
5
|
+
Sprite as PixiSprite,
|
|
6
|
+
Rectangle,
|
|
7
|
+
Texture,
|
|
8
|
+
} from "pixi.js";
|
|
9
|
+
import { Subscription } from "rxjs";
|
|
10
|
+
import {
|
|
11
|
+
Element,
|
|
12
|
+
createComponent,
|
|
13
|
+
registerComponent,
|
|
14
|
+
} from "../engine/reactive";
|
|
15
|
+
import { arrayEquals, isFunction } from "../engine/utils";
|
|
16
|
+
import { DisplayObject } from "./DisplayObject";
|
|
17
|
+
import {
|
|
18
|
+
AnimationFrames,
|
|
19
|
+
FrameOptions,
|
|
20
|
+
SpritesheetOptions,
|
|
21
|
+
TextureOptions,
|
|
22
|
+
TransformOptions,
|
|
23
|
+
} from "./types/Spritesheet";
|
|
24
|
+
import { ComponentFunction } from "../engine/signal";
|
|
25
|
+
import { DisplayObjectProps } from "./types/DisplayObject";
|
|
26
|
+
import { AnimatedSignal, isAnimatedSignal } from "../engine/animation";
|
|
27
|
+
|
|
28
|
+
const log = console.log;
|
|
29
|
+
|
|
30
|
+
type Image = { image: string };
|
|
31
|
+
|
|
32
|
+
type TextureOptionsMerging = TextureOptions & {
|
|
33
|
+
spriteWidth: number;
|
|
34
|
+
spriteHeight: number;
|
|
35
|
+
sound?: string;
|
|
36
|
+
} & Image &
|
|
37
|
+
TransformOptions;
|
|
38
|
+
|
|
39
|
+
type FrameOptionsMerging = TextureOptionsMerging & FrameOptions;
|
|
40
|
+
type SpritesheetOptionsMerging = TextureOptionsMerging & SpritesheetOptions;
|
|
41
|
+
type TransformOptionsAsArray = Pick<
|
|
42
|
+
TransformOptions,
|
|
43
|
+
"anchor" | "scale" | "skew" | "pivot"
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
type AnimationDataFrames = {
|
|
47
|
+
sprites: FrameOptionsMerging[];
|
|
48
|
+
frames: Texture[][];
|
|
49
|
+
name: string;
|
|
50
|
+
animations: AnimationFrames;
|
|
51
|
+
params: any[];
|
|
52
|
+
data: TextureOptionsMerging;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export enum StandardAnimation {
|
|
56
|
+
Stand = "stand",
|
|
57
|
+
Walk = "walk",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class CanvasSprite extends DisplayObject(PixiSprite) {
|
|
61
|
+
public hitbox: { w: number; h: number };
|
|
62
|
+
public applyTransform: (
|
|
63
|
+
frame: FrameOptionsMerging,
|
|
64
|
+
data: TextureOptionsMerging,
|
|
65
|
+
spritesheet: SpritesheetOptionsMerging
|
|
66
|
+
) => Partial<FrameOptionsMerging>;
|
|
67
|
+
private spritesheet: SpritesheetOptionsMerging;
|
|
68
|
+
private currentAnimation: AnimationDataFrames | null = null;
|
|
69
|
+
private time: number = 0;
|
|
70
|
+
private frameIndex: number = 0;
|
|
71
|
+
private animations: Map<string, AnimationDataFrames> = new Map();
|
|
72
|
+
private subscriptionTick: Subscription;
|
|
73
|
+
private subscriptionSheet: Subscription[] = [];
|
|
74
|
+
private sheetParams: any = {};
|
|
75
|
+
private sheetCurrentAnimation: string = StandardAnimation.Stand;
|
|
76
|
+
onFinish: () => void;
|
|
77
|
+
|
|
78
|
+
private currentAnimationContainer: Container | null = null;
|
|
79
|
+
|
|
80
|
+
private async createTextures(
|
|
81
|
+
options: Required<TextureOptionsMerging>
|
|
82
|
+
): Promise<Texture[][]> {
|
|
83
|
+
const { width, height, framesHeight, framesWidth, image, offset } = options;
|
|
84
|
+
const texture = await Assets.load(image);
|
|
85
|
+
const spriteWidth = options.spriteWidth;
|
|
86
|
+
const spriteHeight = options.spriteHeight;
|
|
87
|
+
const frames: Texture[][] = [];
|
|
88
|
+
const offsetX = (offset && offset.x) || 0;
|
|
89
|
+
const offsetY = (offset && offset.y) || 0;
|
|
90
|
+
for (let i = 0; i < framesHeight; i++) {
|
|
91
|
+
frames[i] = [];
|
|
92
|
+
for (let j = 0; j < framesWidth; j++) {
|
|
93
|
+
const rectX = j * spriteWidth + offsetX;
|
|
94
|
+
const rectY = i * spriteHeight + offsetY;
|
|
95
|
+
if (rectY > height) {
|
|
96
|
+
throw log(
|
|
97
|
+
`Warning, there is a problem with the height of the "${this.id}" spritesheet. When cutting into frames, the frame exceeds the height of the image.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (rectX > width) {
|
|
101
|
+
throw log(
|
|
102
|
+
`Warning, there is a problem with the width of the "${this.id}" spritesheet. When cutting into frames, the frame exceeds the width of the image.`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
frames[i].push(
|
|
106
|
+
new Texture({
|
|
107
|
+
source: texture.source,
|
|
108
|
+
frame: new Rectangle(rectX, rectY, spriteWidth, spriteHeight),
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return frames;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async createAnimations() {
|
|
117
|
+
const { textures } = this.spritesheet;
|
|
118
|
+
if (!textures) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
for (let animationName in textures) {
|
|
122
|
+
const props: (keyof TextureOptionsMerging)[] = [
|
|
123
|
+
"width",
|
|
124
|
+
"height",
|
|
125
|
+
"framesHeight",
|
|
126
|
+
"framesWidth",
|
|
127
|
+
"rectWidth",
|
|
128
|
+
"rectHeight",
|
|
129
|
+
"offset",
|
|
130
|
+
"image",
|
|
131
|
+
"sound",
|
|
132
|
+
];
|
|
133
|
+
const parentObj = props.reduce(
|
|
134
|
+
(prev, val) => ({ ...prev, [val]: this.spritesheet[val] }),
|
|
135
|
+
{}
|
|
136
|
+
);
|
|
137
|
+
const optionsTextures: TextureOptionsMerging = {
|
|
138
|
+
...parentObj,
|
|
139
|
+
...textures[animationName],
|
|
140
|
+
} as any;
|
|
141
|
+
const {
|
|
142
|
+
rectWidth,
|
|
143
|
+
width = 0,
|
|
144
|
+
framesWidth = 1,
|
|
145
|
+
rectHeight,
|
|
146
|
+
height = 0,
|
|
147
|
+
framesHeight = 1,
|
|
148
|
+
} = optionsTextures;
|
|
149
|
+
optionsTextures.spriteWidth = rectWidth ? rectWidth : width / framesWidth;
|
|
150
|
+
optionsTextures.spriteHeight = rectHeight
|
|
151
|
+
? rectHeight
|
|
152
|
+
: height / framesHeight;
|
|
153
|
+
this.animations.set(animationName, {
|
|
154
|
+
frames: await this.createTextures(
|
|
155
|
+
optionsTextures as Required<TextureOptionsMerging>
|
|
156
|
+
),
|
|
157
|
+
name: animationName,
|
|
158
|
+
animations: textures[animationName].animations,
|
|
159
|
+
params: [],
|
|
160
|
+
data: optionsTextures,
|
|
161
|
+
sprites: [],
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async onMount(params: Element<CanvasSprite>) {
|
|
167
|
+
const { props, propObservables } = params;
|
|
168
|
+
const tick: Signal = props.context.tick;
|
|
169
|
+
const sheet = props.sheet ?? {};
|
|
170
|
+
if (sheet?.onFinish) {
|
|
171
|
+
this.onFinish = sheet.onFinish;
|
|
172
|
+
}
|
|
173
|
+
this.subscriptionTick = tick.observable.subscribe((value) => {
|
|
174
|
+
this.update(value);
|
|
175
|
+
});
|
|
176
|
+
if (props.sheet?.definition) {
|
|
177
|
+
this.spritesheet = props.sheet.definition;
|
|
178
|
+
await this.createAnimations();
|
|
179
|
+
}
|
|
180
|
+
if (sheet.params) {
|
|
181
|
+
for (let key in propObservables?.sheet["params"]) {
|
|
182
|
+
const value = propObservables?.sheet["params"][key] as Signal;
|
|
183
|
+
if (isSignal(value)) {
|
|
184
|
+
this.subscriptionSheet.push(
|
|
185
|
+
value.observable.subscribe((value) => {
|
|
186
|
+
if (this.animations.size == 0) return;
|
|
187
|
+
this.play(this.sheetCurrentAnimation, [{ [key]: value }]);
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
} else {
|
|
191
|
+
this.play(this.sheetCurrentAnimation, [{ [key]: value }]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const isMoving = computed(() => {
|
|
197
|
+
const { x, y } = propObservables ?? {};
|
|
198
|
+
if (!x || !y) return false;
|
|
199
|
+
const xSignal = x as AnimatedSignal<any>;
|
|
200
|
+
const ySignal = y as AnimatedSignal<any>;
|
|
201
|
+
const isMovingX =
|
|
202
|
+
isAnimatedSignal(xSignal) &&
|
|
203
|
+
xSignal.animatedState().current !== xSignal.animatedState().end;
|
|
204
|
+
const isMovingY =
|
|
205
|
+
isAnimatedSignal(ySignal) &&
|
|
206
|
+
ySignal.animatedState().current !== ySignal.animatedState().end;
|
|
207
|
+
return isMovingX || isMovingY;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
effect(() => {
|
|
211
|
+
const _isMoving = isMoving();
|
|
212
|
+
|
|
213
|
+
if (!this.isMounted) return;
|
|
214
|
+
|
|
215
|
+
if (_isMoving) {
|
|
216
|
+
this.sheetCurrentAnimation = StandardAnimation.Walk;
|
|
217
|
+
} else {
|
|
218
|
+
this.sheetCurrentAnimation = StandardAnimation.Stand;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
this.play(this.sheetCurrentAnimation, [this.sheetParams]);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
super.onMount(params);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async onUpdate(props) {
|
|
228
|
+
super.onUpdate(props);
|
|
229
|
+
|
|
230
|
+
const sheet = props.sheet;
|
|
231
|
+
if (sheet?.params) this.sheetParams = sheet?.params;
|
|
232
|
+
|
|
233
|
+
if (sheet?.playing && this.isMounted) {
|
|
234
|
+
this.sheetCurrentAnimation = sheet?.playing;
|
|
235
|
+
this.play(this.sheetCurrentAnimation, [this.sheetParams]);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (props.hitbox) this.hitbox = props.hitbox;
|
|
239
|
+
|
|
240
|
+
if (props.scaleMode) this.baseTexture.scaleMode = props.scaleMode;
|
|
241
|
+
else if (props.image && this.fullProps.rectangle === undefined) {
|
|
242
|
+
this.texture = await Assets.load(this.fullProps.image);
|
|
243
|
+
} else if (props.texture) {
|
|
244
|
+
this.texture = props.texture;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (props.rectangle !== undefined) {
|
|
248
|
+
const { x, y, width, height } = props.rectangle?.value ?? props.rectangle;
|
|
249
|
+
const texture = await Assets.load(this.fullProps.image);
|
|
250
|
+
this.texture = new Texture({
|
|
251
|
+
source: texture.source,
|
|
252
|
+
frame: new Rectangle(x, y, width, height),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
onDestroy(): void {
|
|
258
|
+
super.onDestroy();
|
|
259
|
+
this.subscriptionSheet.forEach((sub) => sub.unsubscribe());
|
|
260
|
+
this.subscriptionTick.unsubscribe();
|
|
261
|
+
if (this.currentAnimationContainer && this.parent instanceof Container) {
|
|
262
|
+
this.parent.removeChild(this.currentAnimationContainer);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
has(name: string): boolean {
|
|
267
|
+
return this.animations.has(name);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
get(name: string): AnimationDataFrames {
|
|
271
|
+
return this.animations.get(name) as AnimationDataFrames;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
isPlaying(name?: string): boolean {
|
|
275
|
+
if (!name) return !!this.currentAnimation;
|
|
276
|
+
if (this.currentAnimation == null) return false;
|
|
277
|
+
return this.currentAnimation.name == name;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
stop() {
|
|
281
|
+
this.currentAnimation = null;
|
|
282
|
+
this.destroy();
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
play(name: string, params: any[] = []) {
|
|
286
|
+
const animParams = this.currentAnimation?.params;
|
|
287
|
+
|
|
288
|
+
if (this.isPlaying(name) && arrayEquals(params, animParams || [])) return;
|
|
289
|
+
|
|
290
|
+
const animation = this.get(name);
|
|
291
|
+
|
|
292
|
+
if (!animation) {
|
|
293
|
+
throw new Error(
|
|
294
|
+
`Impossible to play the ${name} animation because it doesn't exist on the "${this.id}" spritesheet`
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const cloneParams = structuredClone(params);
|
|
299
|
+
|
|
300
|
+
this.removeChildren();
|
|
301
|
+
animation.sprites = [];
|
|
302
|
+
this.currentAnimation = animation;
|
|
303
|
+
this.currentAnimation.params = cloneParams;
|
|
304
|
+
this.time = 0;
|
|
305
|
+
this.frameIndex = 0;
|
|
306
|
+
let animations: any = animation.animations;
|
|
307
|
+
animations = isFunction(animations)
|
|
308
|
+
? (animations as Function)(...cloneParams)
|
|
309
|
+
: animations;
|
|
310
|
+
|
|
311
|
+
this.currentAnimationContainer = new Container();
|
|
312
|
+
|
|
313
|
+
for (let container of animations as FrameOptionsMerging[][]) {
|
|
314
|
+
const sprite = new PixiSprite();
|
|
315
|
+
for (let frame of container) {
|
|
316
|
+
this.currentAnimation.sprites.push(frame);
|
|
317
|
+
}
|
|
318
|
+
this.currentAnimationContainer.addChild(sprite);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const sound = this.currentAnimation.data.sound;
|
|
322
|
+
|
|
323
|
+
if (sound) {
|
|
324
|
+
//RpgSound.get(sound).play()
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Updates immediately to avoid flickering
|
|
328
|
+
this.update({
|
|
329
|
+
deltaRatio: 1,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
update({ deltaRatio }) {
|
|
334
|
+
if (
|
|
335
|
+
!this.isPlaying() ||
|
|
336
|
+
!this.currentAnimation ||
|
|
337
|
+
!this.currentAnimationContainer
|
|
338
|
+
)
|
|
339
|
+
return;
|
|
340
|
+
|
|
341
|
+
const self = this;
|
|
342
|
+
const { frames, sprites, data } = this.currentAnimation;
|
|
343
|
+
let frame = sprites[this.frameIndex];
|
|
344
|
+
const nextFrame = sprites[this.frameIndex + 1];
|
|
345
|
+
|
|
346
|
+
for (let _sprite of this.currentAnimationContainer.children) {
|
|
347
|
+
const sprite = _sprite as PixiSprite;
|
|
348
|
+
|
|
349
|
+
if (!frame || frame.frameY == undefined || frame.frameX == undefined) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
this.texture = frames[frame.frameY][frame.frameX];
|
|
354
|
+
|
|
355
|
+
const getVal = <T extends keyof TransformOptions>(
|
|
356
|
+
prop: T
|
|
357
|
+
): TransformOptions[T] | undefined => {
|
|
358
|
+
return frame[prop] ?? data[prop] ?? this.spritesheet[prop];
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const applyTransform = <T extends keyof TransformOptionsAsArray>(
|
|
362
|
+
prop: T
|
|
363
|
+
): void => {
|
|
364
|
+
const val = getVal<T>(prop);
|
|
365
|
+
if (val) {
|
|
366
|
+
this[prop as string].set(...val!);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
function applyTransformValue<T extends keyof TransformOptions>(prop: T);
|
|
371
|
+
function applyTransformValue<T extends keyof TransformOptions>(
|
|
372
|
+
prop: string,
|
|
373
|
+
alias: T
|
|
374
|
+
);
|
|
375
|
+
function applyTransformValue<T extends keyof TransformOptions>(
|
|
376
|
+
prop: T,
|
|
377
|
+
alias?: T
|
|
378
|
+
): void {
|
|
379
|
+
const optionProp = alias || prop;
|
|
380
|
+
const val = getVal<T>(optionProp);
|
|
381
|
+
if (val !== undefined) {
|
|
382
|
+
self[prop as string] = val;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (this.applyTransform) {
|
|
387
|
+
frame = {
|
|
388
|
+
...frame,
|
|
389
|
+
...this.applyTransform(frame, data, this.spritesheet),
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const realSize = getVal<"spriteRealSize">("spriteRealSize");
|
|
394
|
+
const heightOfSprite =
|
|
395
|
+
typeof realSize == "number" ? realSize : realSize?.height;
|
|
396
|
+
const widthOfSprite =
|
|
397
|
+
typeof realSize == "number" ? realSize : realSize?.width;
|
|
398
|
+
|
|
399
|
+
const applyAnchorBySize = () => {
|
|
400
|
+
if (heightOfSprite && this.hitbox) {
|
|
401
|
+
const { spriteWidth, spriteHeight } = data;
|
|
402
|
+
const w = (spriteWidth - this.hitbox.w) / 2 / spriteWidth;
|
|
403
|
+
const gap = (spriteHeight - heightOfSprite) / 2;
|
|
404
|
+
const h = (spriteHeight - this.hitbox.h - gap) / spriteHeight;
|
|
405
|
+
this.anchor.set(w, h);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
if (frame.sound) {
|
|
410
|
+
//RpgSound.get(frame.sound).play()
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
applyAnchorBySize();
|
|
414
|
+
|
|
415
|
+
applyTransform("anchor");
|
|
416
|
+
applyTransform("scale");
|
|
417
|
+
applyTransform("skew");
|
|
418
|
+
applyTransform("pivot");
|
|
419
|
+
|
|
420
|
+
applyTransformValue("alpha", "opacity");
|
|
421
|
+
applyTransformValue("x");
|
|
422
|
+
applyTransformValue("y");
|
|
423
|
+
applyTransformValue("angle");
|
|
424
|
+
applyTransformValue("rotation");
|
|
425
|
+
applyTransformValue("visible");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (!nextFrame) {
|
|
429
|
+
this.time = 0;
|
|
430
|
+
this.frameIndex = 0;
|
|
431
|
+
if (this.onFinish && sprites.length > 1) this.onFinish();
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
this.time += deltaRatio ?? 1;
|
|
436
|
+
|
|
437
|
+
if (this.time >= nextFrame.time) {
|
|
438
|
+
this.frameIndex++;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export interface CanvasSprite extends PixiSprite {}
|
|
444
|
+
|
|
445
|
+
registerComponent("Sprite", CanvasSprite);
|
|
446
|
+
|
|
447
|
+
// Define the props interface for Sprite
|
|
448
|
+
export interface SpriteProps extends DisplayObjectProps {
|
|
449
|
+
sheet?: {
|
|
450
|
+
definition?: SpritesheetOptionsMerging;
|
|
451
|
+
playing?: string;
|
|
452
|
+
params?: any;
|
|
453
|
+
onFinish?: () => void;
|
|
454
|
+
};
|
|
455
|
+
scaleMode?: number;
|
|
456
|
+
image?: string;
|
|
457
|
+
rectangle?: {
|
|
458
|
+
x: number;
|
|
459
|
+
y: number;
|
|
460
|
+
width: number;
|
|
461
|
+
height: number;
|
|
462
|
+
};
|
|
463
|
+
context?: {
|
|
464
|
+
tick: Signal;
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export interface SpritePropsWithImage extends Omit<SpriteProps, "sheet"> {
|
|
469
|
+
image: string;
|
|
470
|
+
rectangle?: {
|
|
471
|
+
x: number;
|
|
472
|
+
y: number;
|
|
473
|
+
width: number;
|
|
474
|
+
height: number;
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export interface SpritePropsWithSheet
|
|
479
|
+
extends Omit<SpriteProps, "image" | "rectangle"> {
|
|
480
|
+
sheet: {
|
|
481
|
+
definition: SpritesheetOptionsMerging;
|
|
482
|
+
playing?: string;
|
|
483
|
+
params?: any;
|
|
484
|
+
onFinish?: () => void;
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export type SpritePropTypes = SpritePropsWithImage | SpritePropsWithSheet;
|
|
489
|
+
|
|
490
|
+
// Update the Sprite function to use the props interface
|
|
491
|
+
export const Sprite: ComponentFunction<SpritePropTypes> = (props) => {
|
|
492
|
+
return createComponent("Sprite", props);
|
|
493
|
+
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Text as PixiText, TextStyle } from "pixi.js";
|
|
2
|
+
import { createComponent, registerComponent } from "../engine/reactive";
|
|
3
|
+
import { DisplayObject } from "./DisplayObject";
|
|
4
|
+
import { DisplayObjectProps } from "./types/DisplayObject";
|
|
5
|
+
import { Signal } from "@signe/reactive";
|
|
6
|
+
import { on } from "../engine/trigger";
|
|
7
|
+
|
|
8
|
+
enum TextEffect {
|
|
9
|
+
Typewriter = "typewriter",
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface TextProps extends DisplayObjectProps {
|
|
13
|
+
text?: string;
|
|
14
|
+
style?: Partial<TextStyle>;
|
|
15
|
+
color?: string;
|
|
16
|
+
size?: string;
|
|
17
|
+
fontFamily?: string;
|
|
18
|
+
typewriter?: {
|
|
19
|
+
speed?: number;
|
|
20
|
+
start?: () => void;
|
|
21
|
+
onComplete?: () => void;
|
|
22
|
+
skip?: () => void;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class CanvasText extends DisplayObject(PixiText) {
|
|
27
|
+
private subscriptionTick: any;
|
|
28
|
+
private fullText: string = "";
|
|
29
|
+
private currentIndex: number = 0;
|
|
30
|
+
private typewriterSpeed: number = 1; // Default speed
|
|
31
|
+
private _wordWrapWidth: number = 0;
|
|
32
|
+
private typewriterOptions: any = {};
|
|
33
|
+
private skipSignal?: () => void;
|
|
34
|
+
|
|
35
|
+
onMount(args) {
|
|
36
|
+
super.onMount(args);
|
|
37
|
+
const { props } = args;
|
|
38
|
+
const tick: Signal = props.context.tick;
|
|
39
|
+
|
|
40
|
+
if (props.text && props.typewriter) {
|
|
41
|
+
this.fullText = props.text;
|
|
42
|
+
this.text = "";
|
|
43
|
+
this.currentIndex = 0;
|
|
44
|
+
// Set typewriter options
|
|
45
|
+
if (props.typewriter) {
|
|
46
|
+
this.typewriterOptions = props.typewriter;
|
|
47
|
+
if (this.typewriterOptions.skip) {
|
|
48
|
+
on(this.typewriterOptions.skip, () => {
|
|
49
|
+
this.skipTypewriter();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
this.subscriptionTick = tick.observable.subscribe(() => {
|
|
55
|
+
if (props.typewriter) {
|
|
56
|
+
this.typewriterEffect();
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
onUpdate(props: TextProps) {
|
|
62
|
+
super.onUpdate(props);
|
|
63
|
+
if (props.typewriter) {
|
|
64
|
+
if (props.typewriter) {
|
|
65
|
+
this.typewriterOptions = props.typewriter;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (props.text) {
|
|
69
|
+
this.text = props.text;
|
|
70
|
+
}
|
|
71
|
+
if (props.text !== undefined && props.text !== this.fullText && this.fullProps.typewriter) {
|
|
72
|
+
this.text = "";
|
|
73
|
+
this.currentIndex = 0;
|
|
74
|
+
this.fullText = props.text;
|
|
75
|
+
}
|
|
76
|
+
if (props.style) {
|
|
77
|
+
for (const key in props.style) {
|
|
78
|
+
this.style[key] = props.style[key];
|
|
79
|
+
}
|
|
80
|
+
if (props.style.wordWrapWidth) {
|
|
81
|
+
this._wordWrapWidth = props.style.wordWrapWidth;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (props.color) {
|
|
85
|
+
this.style.fill = props.color;
|
|
86
|
+
}
|
|
87
|
+
if (props.size) {
|
|
88
|
+
this.style.fontSize = props.size;
|
|
89
|
+
}
|
|
90
|
+
if (props.fontFamily) {
|
|
91
|
+
this.style.fontFamily = props.fontFamily;
|
|
92
|
+
}
|
|
93
|
+
if (this._wordWrapWidth) {
|
|
94
|
+
this.setWidth(this._wordWrapWidth);
|
|
95
|
+
} else {
|
|
96
|
+
this.setWidth(this.width);
|
|
97
|
+
}
|
|
98
|
+
this.setHeight(this.height);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get onCompleteCallback() {
|
|
102
|
+
return this.typewriterOptions.onComplete;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private typewriterEffect() {
|
|
106
|
+
if (this.currentIndex < this.fullText.length) {
|
|
107
|
+
const nextIndex = Math.min(
|
|
108
|
+
this.currentIndex + (this.typewriterOptions.speed ?? 1),
|
|
109
|
+
this.fullText.length
|
|
110
|
+
);
|
|
111
|
+
this.text = this.fullText.slice(0, nextIndex);
|
|
112
|
+
this.currentIndex = nextIndex;
|
|
113
|
+
|
|
114
|
+
// Check if typewriter effect is complete
|
|
115
|
+
if (
|
|
116
|
+
this.currentIndex === this.fullText.length &&
|
|
117
|
+
this.onCompleteCallback
|
|
118
|
+
) {
|
|
119
|
+
this.onCompleteCallback();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Add a method to skip the typewriter effect
|
|
125
|
+
private skipTypewriter() {
|
|
126
|
+
if (this.skipSignal) {
|
|
127
|
+
this.skipSignal();
|
|
128
|
+
}
|
|
129
|
+
this.text = this.fullText;
|
|
130
|
+
this.currentIndex = this.fullText.length;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
onDestroy(): void {
|
|
134
|
+
super.onDestroy();
|
|
135
|
+
this.subscriptionTick.unsubscribe();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface CanvasText extends PixiText {}
|
|
140
|
+
|
|
141
|
+
registerComponent("Text", CanvasText);
|
|
142
|
+
|
|
143
|
+
export function Text(props: TextProps) {
|
|
144
|
+
return createComponent("Text", props);
|
|
145
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { CompositeTilemap } from "@pixi/tilemap";
|
|
2
|
+
import { Tile as TiledTileClass } from '@rpgjs/tiled';
|
|
3
|
+
import { AnimatedSprite, Texture, groupD8 } from "pixi.js";
|
|
4
|
+
import { TileSet } from "./TileSet";
|
|
5
|
+
|
|
6
|
+
export class Tile extends AnimatedSprite {
|
|
7
|
+
static getTextures(tile: TiledTileClass, tileSet: TileSet) {
|
|
8
|
+
const textures: Texture[] = [];
|
|
9
|
+
|
|
10
|
+
if (tile.animations && tile.animations.length) {
|
|
11
|
+
tile.animations.forEach(frame => {
|
|
12
|
+
textures.push(tileSet.textures[frame.tileid])
|
|
13
|
+
});
|
|
14
|
+
} else {
|
|
15
|
+
textures.push(tileSet.textures[tile.gid - tileSet.firstgid])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return textures;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
animations: { tileid: number, duration: number }[] = []
|
|
22
|
+
_x: number = 0
|
|
23
|
+
_y: number = 0
|
|
24
|
+
pointsBufIndex: number
|
|
25
|
+
properties: any = {}
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
private tile: TiledTileClass,
|
|
29
|
+
private tileSet: TileSet
|
|
30
|
+
) {
|
|
31
|
+
super(Tile.getTextures(tile, tileSet));
|
|
32
|
+
this.animations = tile.animations || []
|
|
33
|
+
this.properties = tile.properties
|
|
34
|
+
this.textures = Tile.getTextures(tile, tileSet)
|
|
35
|
+
this.texture = this.textures[0] as Texture
|
|
36
|
+
this.flip()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get gid() {
|
|
40
|
+
return this.tile.gid
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setAnimation(frame: CompositeTilemap) {
|
|
44
|
+
const size = this.animations.length
|
|
45
|
+
if (size > 1) {
|
|
46
|
+
const offset = (this.animations[1].tileid - this.animations[0].tileid) * this.width
|
|
47
|
+
frame.tileAnimX(offset, size)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
flip() {
|
|
52
|
+
let symmetry
|
|
53
|
+
let i = 0
|
|
54
|
+
const add = (symmetrySecond) => {
|
|
55
|
+
i++
|
|
56
|
+
if (symmetry) symmetry = groupD8.add(symmetry, symmetrySecond)
|
|
57
|
+
else symmetry = symmetrySecond
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (this.tile.horizontalFlip) {
|
|
61
|
+
add(groupD8.MIRROR_HORIZONTAL)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (this.tile.verticalFlip) {
|
|
65
|
+
add(groupD8.MIRROR_VERTICAL)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (this.tile.diagonalFlip) {
|
|
69
|
+
if (i % 2 == 0) {
|
|
70
|
+
add(groupD8.MAIN_DIAGONAL)
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
add(groupD8.REVERSE_DIAGONAL)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
//if (symmetry) this.texture.rotate = symmetry
|
|
78
|
+
}
|
|
79
|
+
}
|