Coroutine 类型从IEnumerable为Iterator
This commit is contained in:
106
source/src/ECS/Components/Renderables/Mesh.ts
Normal file
106
source/src/ECS/Components/Renderables/Mesh.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
///<reference path="RenderableComponent.ts"/>
|
||||
module es {
|
||||
/**
|
||||
* 可用于创建简单网格的基本类
|
||||
*/
|
||||
export class Mesh extends RenderableComponent {
|
||||
public displayObject: egret.Mesh = new egret.Mesh();
|
||||
public get bounds(){
|
||||
if (this._areBoundsDirty){
|
||||
this._bounds.calculateBounds(Vector2.add(this.entity.transform.position, this._topLeftVertPosition), Vector2.zero,
|
||||
Vector2.zero, this.entity.transform.scale, this.entity.transform.rotation, this._width, this._height);
|
||||
this._areBoundsDirty = false;
|
||||
}
|
||||
|
||||
return this._bounds;
|
||||
}
|
||||
|
||||
public _primitiveCount: number = 0;
|
||||
public _topLeftVertPosition: Vector2;
|
||||
public _width: number = 0;
|
||||
public _height: number = 0;
|
||||
public _triangles: number[] = [];
|
||||
public _verts: VertexPositionColorTexture[] = [];
|
||||
|
||||
/**
|
||||
* 重新计算边界和可选地设置uv。设置uv以最适合的方式映射纹理。
|
||||
* @param recalculateUVs
|
||||
*/
|
||||
public recalculateBounds(recalculateUVs: boolean){
|
||||
this._topLeftVertPosition = new Vector2(Number.MAX_VALUE, Number.MAX_VALUE);
|
||||
let max = new Vector2(Number.MIN_VALUE, Number.MIN_VALUE);
|
||||
|
||||
for (let i = 0; i < this._verts.length; i ++){
|
||||
this._topLeftVertPosition.x = Math.min(this._topLeftVertPosition.x, this._verts[i].position.x);
|
||||
this._topLeftVertPosition.y = Math.min(this._topLeftVertPosition.y, this._verts[i].position.y);
|
||||
max.x = Math.max(max.x, this._verts[i].position.x);
|
||||
max.y = Math.max(max.y, this._verts[i].position.y);
|
||||
}
|
||||
|
||||
this._width = max.x - this._topLeftVertPosition.x;
|
||||
this._height = max.y - this._topLeftVertPosition.y;
|
||||
|
||||
// 如果需要处理uv
|
||||
if (recalculateUVs){
|
||||
for (let i = 0; i < this._verts.length; i ++){
|
||||
this._verts[i].textureCoordinate.x = (this._verts[i].position.x - this._topLeftVertPosition.x) / this._width;
|
||||
this._verts[i].textureCoordinate.y = (this._verts[i].position.y - this._topLeftVertPosition.y) / this._height;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置纹理。传入null来取消纹理设置。
|
||||
* @param texture
|
||||
*/
|
||||
public setTexture(texture: egret.Texture): Mesh{
|
||||
this.displayObject.texture = texture;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置vert位置。如果position数组与vert数组大小不匹配,则将重新创建vert数组。
|
||||
* @param positions
|
||||
*/
|
||||
public setVertPositions(positions: Vector2[]){
|
||||
if (this._verts == undefined || this._verts.length != positions.length){
|
||||
this._verts = new Array(positions.length);
|
||||
this._verts.fill(new VertexPositionColorTexture(), 0, positions.length);
|
||||
}
|
||||
|
||||
for (let i = 0; i < this._verts.length; i ++){
|
||||
this._verts[i].position = positions[i];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置渲染的三角形索引
|
||||
* @param triangles
|
||||
*/
|
||||
public setTriangles(triangles: number[]){
|
||||
if (triangles.length % 3 != 0){
|
||||
console.error("三角形必须是3的倍数");
|
||||
return;
|
||||
}
|
||||
|
||||
this._primitiveCount = triangles.length / 3;
|
||||
this._triangles = triangles;
|
||||
return this;
|
||||
}
|
||||
|
||||
public render(camera: es.Camera) {
|
||||
let renderNode = this.displayObject.$renderNode as egret.sys.MeshNode;
|
||||
renderNode.imageWidth = this._width;
|
||||
renderNode.imageHeight = this._height;
|
||||
renderNode.vertices = this._triangles;
|
||||
}
|
||||
}
|
||||
|
||||
export class VertexPositionColorTexture {
|
||||
public position: Vector2;
|
||||
public textureCoordinate: Vector2;
|
||||
}
|
||||
}
|
||||
231
source/src/ECS/Components/Renderables/RenderableComponent.ts
Normal file
231
source/src/ECS/Components/Renderables/RenderableComponent.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
///<reference path="../PooledComponent.ts" />
|
||||
module es {
|
||||
/**
|
||||
* 所有可渲染组件的基类
|
||||
*/
|
||||
export abstract class RenderableComponent extends Component implements IRenderable {
|
||||
/**
|
||||
* 用于装载egret显示对象
|
||||
*/
|
||||
public displayObject: egret.DisplayObject = new egret.DisplayObject();
|
||||
public hollowShape: egret.Shape = new egret.Shape();
|
||||
public pixelShape: egret.Shape = new egret.Shape();
|
||||
/**
|
||||
* 用于着色器处理精灵
|
||||
*/
|
||||
public color: number = 0x000000;
|
||||
protected _areBoundsDirty = true;
|
||||
|
||||
/**
|
||||
* renderableComponent的宽度
|
||||
* 如果你不重写bounds属性则需要实现这个
|
||||
*/
|
||||
public get width() {
|
||||
return this.bounds.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* renderableComponent的高度
|
||||
* 如果你不重写bounds属性则需要实现这个
|
||||
*/
|
||||
public get height() {
|
||||
return this.bounds.height;
|
||||
}
|
||||
|
||||
public debugRenderEnabled: boolean = true;
|
||||
protected _localOffset: Vector2 = Vector2.zero;
|
||||
|
||||
/**
|
||||
* 从父实体的偏移量。用于向需要特定定位的实体
|
||||
*/
|
||||
public get localOffset(): Vector2 {
|
||||
return this._localOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从父实体的偏移量。用于向需要特定定位的实体
|
||||
* @param value
|
||||
*/
|
||||
public set localOffset(value: Vector2) {
|
||||
this.setLocalOffset(value);
|
||||
}
|
||||
|
||||
protected _renderLayer: number = 0;
|
||||
|
||||
/**
|
||||
* 较低的渲染层在前面,较高的在后面
|
||||
*/
|
||||
public get renderLayer(): number {
|
||||
return this._renderLayer;
|
||||
}
|
||||
|
||||
public set renderLayer(value: number) {
|
||||
this.setRenderLayer(value);
|
||||
}
|
||||
|
||||
protected _bounds: Rectangle = new Rectangle();
|
||||
|
||||
/**
|
||||
* 这个物体的AABB, 用于相机剔除
|
||||
*/
|
||||
public get bounds(): Rectangle {
|
||||
if (this._areBoundsDirty) {
|
||||
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, Vector2.zero,
|
||||
this.entity.transform.scale, this.entity.transform.rotation, this.width, this.height);
|
||||
this._areBoundsDirty = false;
|
||||
}
|
||||
|
||||
return this._bounds;
|
||||
}
|
||||
|
||||
private _isVisible: boolean;
|
||||
|
||||
/**
|
||||
* 可渲染的可见性。状态的改变会调用onBecameVisible/onBecameInvisible方法
|
||||
*/
|
||||
public get isVisible() {
|
||||
return this._isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* 可渲染的可见性。状态的改变会调用onBecameVisible/onBecameInvisible方法
|
||||
* @param value
|
||||
*/
|
||||
public set isVisible(value: boolean) {
|
||||
if (this._isVisible != value) {
|
||||
this._isVisible = value;
|
||||
|
||||
if (this._isVisible)
|
||||
this.onBecameVisible();
|
||||
else
|
||||
this.onBecameInvisible();
|
||||
}
|
||||
}
|
||||
|
||||
public onEntityTransformChanged(comp: transform.Component) {
|
||||
this._areBoundsDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 由渲染器调用。可以使用摄像机进行剔除
|
||||
* @param camera
|
||||
*/
|
||||
public abstract render(camera: Camera);
|
||||
|
||||
public debugRender(camera: Camera) {
|
||||
if (!this.debugRenderEnabled)
|
||||
return;
|
||||
|
||||
if (!this.hollowShape.parent)
|
||||
this.debugDisplayObject.addChild(this.hollowShape);
|
||||
|
||||
if (!this.pixelShape.parent)
|
||||
this.debugDisplayObject.addChild(this.pixelShape);
|
||||
|
||||
if (!this.entity.getComponent(Collider)){
|
||||
this.hollowShape.graphics.clear();
|
||||
this.hollowShape.graphics.beginFill(Colors.renderableBounds, 0);
|
||||
this.hollowShape.graphics.lineStyle(1, Colors.renderableBounds);
|
||||
this.hollowShape.graphics.drawRect(this.bounds.x - camera.bounds.x, this.bounds.y - camera.bounds.y, this.bounds.width, this.bounds.height);
|
||||
this.hollowShape.graphics.endFill();
|
||||
}
|
||||
|
||||
let pixelPos = Vector2.add(this.entity.transform.position, this._localOffset).subtract(camera.bounds.location);
|
||||
this.pixelShape.graphics.clear();
|
||||
this.pixelShape.graphics.beginFill(Colors.renderableCenter, 0);
|
||||
this.pixelShape.graphics.lineStyle(4, Colors.renderableCenter);
|
||||
this.pixelShape.graphics.moveTo(pixelPos.x, pixelPos.y);
|
||||
this.pixelShape.graphics.lineTo(pixelPos.x, pixelPos.y);
|
||||
this.pixelShape.graphics.endFill();
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果renderableComponent的边界与camera.bounds相交 返回true
|
||||
* 用于处理isVisible标志的状态开关
|
||||
* 在渲染方法中使用这个方法来决定是否渲染
|
||||
* @param camera
|
||||
*/
|
||||
public isVisibleFromCamera(camera: Camera): boolean {
|
||||
if (!camera)
|
||||
return false;
|
||||
|
||||
this.isVisible = camera.bounds.intersects(this.bounds);
|
||||
return this.isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* 较低的渲染层在前面,较高的在后面
|
||||
* @param renderLayer
|
||||
*/
|
||||
public setRenderLayer(renderLayer: number): RenderableComponent {
|
||||
if (renderLayer != this._renderLayer) {
|
||||
let oldRenderLayer = this._renderLayer;
|
||||
this._renderLayer = renderLayer;
|
||||
|
||||
// 如果该组件拥有一个实体,那么是由ComponentList管理,需要通知它改变了渲染层
|
||||
if (this.entity && this.entity.scene)
|
||||
this.entity.scene.renderableComponents.updateRenderableRenderLayer(this, oldRenderLayer, this._renderLayer);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于着色器处理精灵
|
||||
* @param color
|
||||
*/
|
||||
public setColor(color: number): RenderableComponent {
|
||||
this.color = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从父实体的偏移量。用于向需要特定定位的实体
|
||||
* @param offset
|
||||
*/
|
||||
public setLocalOffset(offset: Vector2): RenderableComponent {
|
||||
if (this._localOffset != offset) {
|
||||
this._localOffset = offset;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进行状态同步
|
||||
*/
|
||||
public sync(camera: Camera) {
|
||||
if (this.displayObject.x != this.bounds.x - camera.bounds.y) this.displayObject.x = this.bounds.x - camera.bounds.y;
|
||||
if (this.displayObject.y != this.bounds.y - camera.bounds.y) this.displayObject.y = this.bounds.y - camera.bounds.y;
|
||||
if (this.displayObject.scaleX != this.entity.scale.x) this.displayObject.scaleX = this.entity.scale.x;
|
||||
if (this.displayObject.scaleY != this.entity.scale.y) this.displayObject.scaleY = this.entity.scale.y;
|
||||
if (this.displayObject.rotation != this.entity.rotationDegrees) this.displayObject.rotation = this.entity.rotationDegrees;
|
||||
}
|
||||
|
||||
public compareTo(other: RenderableComponent){
|
||||
return other.renderLayer - this.renderLayer;
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return `[RenderableComponent] renderLayer: ${this.renderLayer}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当renderableComponent进入相机框架时调用
|
||||
* 如果渲染器不适用isVisibleFromCamera进行剔除检查 这些方法不会被调用
|
||||
*/
|
||||
protected onBecameVisible() {
|
||||
this.displayObject.visible = this.isVisible;
|
||||
this.debugDisplayObject.visible = this.isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当renderableComponent离开相机框架时调用
|
||||
* 如果渲染器不适用isVisibleFromCamera进行剔除检查 这些方法不会被调用
|
||||
*/
|
||||
protected onBecameInvisible() {
|
||||
this.displayObject.visible = this.isVisible;
|
||||
this.debugDisplayObject.visible = this.isVisible;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
///<reference path="./TiledSpriteRenderer.ts"/>
|
||||
module es {
|
||||
import Bitmap = egret.Bitmap;
|
||||
|
||||
export class ScrollingSpriteRenderer extends TiledSpriteRenderer {
|
||||
/**
|
||||
* x自动滚动速度(以像素/s为单位)
|
||||
*/
|
||||
public scrollSpeedX = 15;
|
||||
/**
|
||||
* 自动滚动的y速度(以像素/s为单位)
|
||||
*/
|
||||
public scroolSpeedY = 0;
|
||||
|
||||
public get textureScale(): Vector2 {
|
||||
return this._textureScale;
|
||||
}
|
||||
|
||||
public set textureScale(value: Vector2){
|
||||
this._textureScale = value;
|
||||
|
||||
// 重新计算我们的inverseTextureScale和源矩形大小
|
||||
this._inverseTexScale = new Vector2(1 / this._textureScale.x, 1 / this._textureScale.y);
|
||||
}
|
||||
|
||||
public set scrollWidth(value: number){
|
||||
this._scrollWidth = value;
|
||||
}
|
||||
|
||||
public get scrollWidth(){
|
||||
return this._scrollWidth;
|
||||
}
|
||||
|
||||
public set scrollHeight(value: number){
|
||||
this._scrollHeight = value;
|
||||
}
|
||||
|
||||
public get scrollHeight(){
|
||||
return this._scrollHeight;
|
||||
}
|
||||
|
||||
private _scrollX = 0;
|
||||
private _scrollY = 0;
|
||||
private _scrollWidth = 0;
|
||||
private _scrollHeight = 0;
|
||||
|
||||
constructor(sprite: Sprite) {
|
||||
super(sprite);
|
||||
|
||||
this._scrollWidth = this.width;
|
||||
this._scrollHeight = this.height;
|
||||
}
|
||||
|
||||
public update() {
|
||||
if (!this.sprite)
|
||||
return;
|
||||
|
||||
this._scrollX += this.scrollSpeedX * Time.deltaTime;
|
||||
this._scrollY += this.scroolSpeedY * Time.deltaTime;
|
||||
|
||||
this._sourceRect.x = Math.floor(this._scrollX);
|
||||
this._sourceRect.y = Math.floor(this._scrollY);
|
||||
this._sourceRect.width = this._scrollWidth + Math.abs(this._scrollX);
|
||||
this._sourceRect.height = this._scrollHeight + Math.abs(this._scrollY);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
source/src/ECS/Components/Renderables/Sprites/Sprite.ts
Normal file
62
source/src/ECS/Components/Renderables/Sprites/Sprite.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
module es {
|
||||
import RenderTexture = egret.RenderTexture;
|
||||
import Bitmap = egret.Bitmap;
|
||||
import SpriteSheet = egret.SpriteSheet;
|
||||
|
||||
export class Sprite {
|
||||
public texture2D: egret.Texture;
|
||||
public readonly sourceRect: Rectangle;
|
||||
public readonly center: Vector2;
|
||||
public origin: Vector2;
|
||||
public readonly uvs: Rectangle = new Rectangle();
|
||||
|
||||
constructor(texture: egret.Texture,
|
||||
sourceRect: Rectangle = new Rectangle(0, 0, texture.textureWidth, texture.textureHeight),
|
||||
origin: Vector2 = sourceRect.getHalfSize()) {
|
||||
this.texture2D = texture;
|
||||
this.sourceRect = sourceRect;
|
||||
this.center = new Vector2(sourceRect.width * 0.5, sourceRect.height * 0.5);
|
||||
this.origin = origin;
|
||||
|
||||
let inverseTexW = 1 / texture.textureWidth;
|
||||
let inverseTexH = 1 / texture.textureHeight;
|
||||
|
||||
this.uvs.x = sourceRect.x * inverseTexW;
|
||||
this.uvs.y = sourceRect.y * inverseTexH;
|
||||
this.uvs.width = sourceRect.width * inverseTexW;
|
||||
this.uvs.height = sourceRect.height * inverseTexH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供一个精灵的列/行等间隔的图集的精灵列表
|
||||
* @param texture
|
||||
* @param cellWidth
|
||||
* @param cellHeight
|
||||
* @param cellOffset 处理时要包含的第一个单元格。基于0的索引
|
||||
* @param maxCellsToInclude 包含的最大单元
|
||||
*/
|
||||
public static spritesFromAtlas(texture: egret.Texture, cellWidth: number, cellHeight: number,
|
||||
cellOffset: number = 0, maxCellsToInclude: number = Number.MAX_VALUE){
|
||||
let sprites: Sprite[] = [];
|
||||
let cols = texture.textureWidth / cellWidth;
|
||||
let rows = texture.textureHeight / cellHeight;
|
||||
let i = 0;
|
||||
let spriteSheet = new SpriteSheet(texture);
|
||||
|
||||
for (let y = 0; y < rows; y ++){
|
||||
for (let x = 0; x < cols; x ++) {
|
||||
if (i++ < cellOffset) continue;
|
||||
|
||||
let texture = spriteSheet.getTexture(`${y}_${x}`);
|
||||
if (!texture)
|
||||
texture = spriteSheet.createTexture(`${y}_${x}`, x * cellWidth, y * cellHeight, cellWidth, cellHeight);
|
||||
sprites.push(new Sprite(texture));
|
||||
|
||||
if (sprites.length == maxCellsToInclude) return sprites;
|
||||
}
|
||||
}
|
||||
|
||||
return sprites;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
module es {
|
||||
export class SpriteAnimation {
|
||||
public readonly sprites: Sprite[];
|
||||
public readonly frameRate: number;
|
||||
|
||||
constructor(sprites: Sprite[], frameRate: number = 10) {
|
||||
this.sprites = sprites;
|
||||
this.frameRate = frameRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
163
source/src/ECS/Components/Renderables/Sprites/SpriteAnimator.ts
Normal file
163
source/src/ECS/Components/Renderables/Sprites/SpriteAnimator.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
///<reference path="./SpriteRenderer.ts" />
|
||||
module es {
|
||||
export enum LoopMode {
|
||||
/** 在一个循环序列[A][B][C][A][B][C][A][B][C]... */
|
||||
loop,
|
||||
/** [A][B][C]然后暂停,设置时间为0 [A] */
|
||||
once,
|
||||
/** [A][B][C]。当它到达终点时,它会继续播放最后一帧,并且不会停止播放 */
|
||||
clampForever,
|
||||
/** 以一个乒乓循环的方式永远播放这个序列 [A][B][C][B][A][B][C][B]... */
|
||||
pingPong,
|
||||
/** 将顺序向前播放一次,然后返回到开始[A][B][C][B][A],然后暂停并设置时间为0 */
|
||||
pingPongOnce,
|
||||
}
|
||||
|
||||
export enum State {
|
||||
none,
|
||||
running,
|
||||
paused,
|
||||
completed,
|
||||
}
|
||||
|
||||
export class SpriteAnimator extends SpriteRenderer {
|
||||
/**
|
||||
* 在动画完成时触发,包括动画名称
|
||||
*/
|
||||
public onAnimationCompletedEvent: (string) => {};
|
||||
/**
|
||||
* 动画播放速度
|
||||
*/
|
||||
public speed = 1;
|
||||
/**
|
||||
* 动画的当前状态
|
||||
*/
|
||||
public animationState = State.none;
|
||||
/**
|
||||
* 当前动画
|
||||
*/
|
||||
public currentAnimation: SpriteAnimation;
|
||||
/**
|
||||
* 当前动画的名称
|
||||
*/
|
||||
public currentAnimationName: string;
|
||||
/**
|
||||
* 当前动画的精灵数组中当前帧的索引
|
||||
*/
|
||||
public currentFrame: number;
|
||||
public _elapsedTime: number = 0;
|
||||
public _loopMode: LoopMode;
|
||||
|
||||
constructor(sprite?: Sprite) {
|
||||
super(sprite);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查当前动画是否正在运行
|
||||
*/
|
||||
public get isRunning(): boolean {
|
||||
return this.animationState == State.running;
|
||||
}
|
||||
|
||||
private _animations: Map<string, SpriteAnimation> = new Map<string, SpriteAnimation>();
|
||||
|
||||
/** 提供对可用动画列表的访问 */
|
||||
public get animations() {
|
||||
return this._animations;
|
||||
}
|
||||
|
||||
public update() {
|
||||
if (this.animationState != State.running || !this.currentAnimation) return;
|
||||
|
||||
let animation = this.currentAnimation;
|
||||
let secondsPerFrame = 1 / (animation.frameRate * this.speed);
|
||||
let iterationDuration = secondsPerFrame * animation.sprites.length;
|
||||
|
||||
this._elapsedTime += Time.deltaTime;
|
||||
let time = Math.abs(this._elapsedTime);
|
||||
|
||||
// Once和PingPongOnce完成后重置为Time = 0
|
||||
if (this._loopMode == LoopMode.once && time > iterationDuration ||
|
||||
this._loopMode == LoopMode.pingPongOnce && time > iterationDuration * 2) {
|
||||
this.animationState = State.completed;
|
||||
this._elapsedTime = 0;
|
||||
this.currentFrame = 0;
|
||||
(this.displayObject as egret.Bitmap).texture = animation.sprites[this.currentFrame].texture2D;
|
||||
return;
|
||||
}
|
||||
|
||||
// 弄清楚我们在哪个坐标系上
|
||||
let i = Math.floor(time / secondsPerFrame);
|
||||
let n = animation.sprites.length;
|
||||
if (n > 2 && (this._loopMode == LoopMode.pingPong || this._loopMode == LoopMode.pingPongOnce)) {
|
||||
// pingpong
|
||||
let maxIndex = n - 1;
|
||||
this.currentFrame = maxIndex - Math.abs(maxIndex - i % (maxIndex * 2));
|
||||
} else {
|
||||
this.currentFrame = i % n;
|
||||
}
|
||||
|
||||
(this.displayObject as egret.Bitmap).texture = animation.sprites[this.currentFrame].texture2D;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个SpriteAnimation
|
||||
* @param name
|
||||
* @param animation
|
||||
*/
|
||||
public addAnimation(name: string, animation: SpriteAnimation): SpriteAnimator {
|
||||
// 如果我们没有精灵,使用我们找到的第一帧
|
||||
if (!this.sprite && animation.sprites.length > 0)
|
||||
this.setSprite(animation.sprites[0]);
|
||||
this._animations[name] = animation;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以给定的名称放置动画。如果没有指定循环模式,则默认为循环
|
||||
* @param name
|
||||
* @param loopMode
|
||||
*/
|
||||
public play(name: string, loopMode: LoopMode = null) {
|
||||
this.currentAnimation = this._animations[name];
|
||||
this.currentAnimationName = name;
|
||||
this.currentFrame = 0;
|
||||
this.animationState = State.running;
|
||||
(this.displayObject as egret.Bitmap).texture = this.currentAnimation.sprites[0].texture2D;
|
||||
this._elapsedTime = 0;
|
||||
this._loopMode = loopMode ? loopMode : LoopMode.loop;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查动画是否正在播放(即动画是活动的)。它可能仍然处于暂停状态)
|
||||
* @param name
|
||||
*/
|
||||
public isAnimationActive(name: string): boolean {
|
||||
return this.currentAnimation && this.currentAnimationName == name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停动画
|
||||
*/
|
||||
public pause() {
|
||||
this.animationState = State.paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续动画
|
||||
*/
|
||||
public unPause() {
|
||||
this.animationState = State.running;
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止当前动画并将其设为null
|
||||
*/
|
||||
public stop() {
|
||||
this.currentAnimation = null;
|
||||
this.currentAnimationName = null;
|
||||
this.currentFrame = 0;
|
||||
this.animationState = State.none;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
source/src/ECS/Components/Renderables/Sprites/SpriteRenderer.ts
Normal file
131
source/src/ECS/Components/Renderables/Sprites/SpriteRenderer.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
module es {
|
||||
import Bitmap = egret.Bitmap;
|
||||
|
||||
export class SpriteRenderer extends RenderableComponent {
|
||||
constructor(sprite: Sprite | egret.Texture = null) {
|
||||
super();
|
||||
if (sprite instanceof Sprite)
|
||||
this.setSprite(sprite);
|
||||
else if (sprite instanceof egret.Texture)
|
||||
this.setSprite(new Sprite(sprite));
|
||||
}
|
||||
|
||||
public get bounds() {
|
||||
if (this._areBoundsDirty) {
|
||||
if (this._sprite) {
|
||||
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, this._origin,
|
||||
this.entity.transform.scale, this.entity.transform.rotation, this._sprite.sourceRect.width,
|
||||
this._sprite.sourceRect.height);
|
||||
this._areBoundsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
return this._bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用归一化方法设置原点
|
||||
* x/y 均为 0-1
|
||||
*/
|
||||
public get originNormalized(): Vector2 {
|
||||
return new Vector2(this._origin.x / this.width * this.entity.transform.scale.x,
|
||||
this._origin.y / this.height * this.entity.transform.scale.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用归一化方法设置原点
|
||||
* x/y 均为 0-1
|
||||
* @param value
|
||||
*/
|
||||
public set originNormalized(value: Vector2) {
|
||||
this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x,
|
||||
value.y * this.height / this.entity.transform.scale.y));
|
||||
}
|
||||
|
||||
protected _origin: Vector2;
|
||||
|
||||
/**
|
||||
* 精灵的原点。这是在设置精灵时自动设置的
|
||||
*/
|
||||
public get origin(): Vector2 {
|
||||
return this._origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* 精灵的原点。这是在设置精灵时自动设置的
|
||||
* @param value
|
||||
*/
|
||||
public set origin(value: Vector2) {
|
||||
this.setOrigin(value);
|
||||
}
|
||||
|
||||
protected _sprite: Sprite;
|
||||
|
||||
/**
|
||||
* 应该由这个精灵显示的精灵
|
||||
* 当设置时,精灵的原点也被设置为精灵的origin
|
||||
*/
|
||||
public get sprite(): Sprite {
|
||||
return this._sprite;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应该由这个精灵显示的精灵
|
||||
* 当设置时,精灵的原点也被设置为精灵的origin
|
||||
* @param value
|
||||
*/
|
||||
public set sprite(value: Sprite) {
|
||||
this.setSprite(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置精灵并更新精灵的原点以匹配sprite.origin
|
||||
* @param sprite
|
||||
*/
|
||||
public setSprite(sprite: Sprite): SpriteRenderer {
|
||||
this._sprite = sprite;
|
||||
if (this._sprite) {
|
||||
this._origin = this._sprite.origin;
|
||||
this.displayObject.anchorOffsetX = this._origin.x;
|
||||
this.displayObject.anchorOffsetY = this._origin.y;
|
||||
}
|
||||
this.displayObject = new Bitmap(sprite.texture2D);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置可渲染的原点
|
||||
* @param origin
|
||||
*/
|
||||
public setOrigin(origin: Vector2): SpriteRenderer {
|
||||
if (this._origin != origin) {
|
||||
this._origin = origin;
|
||||
this.displayObject.anchorOffsetX = this._origin.x;
|
||||
this.displayObject.anchorOffsetY = this._origin.y;
|
||||
this._areBoundsDirty = true;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用归一化方法设置原点
|
||||
* x/y 均为 0-1
|
||||
* @param value
|
||||
*/
|
||||
public setOriginNormalized(value: Vector2): SpriteRenderer {
|
||||
this.setOrigin(new Vector2(value.x * this.width / this.entity.transform.scale.x,
|
||||
value.y * this.height / this.entity.transform.scale.y));
|
||||
return this;
|
||||
}
|
||||
|
||||
public render(camera: Camera) {
|
||||
this.sync(camera);
|
||||
|
||||
if (this.displayObject.x != this.bounds.x - camera.bounds.x) this.displayObject.x = this.bounds.x - camera.bounds.x;
|
||||
if (this.displayObject.y != this.bounds.y - camera.bounds.y) this.displayObject.y = this.bounds.y - camera.bounds.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
module es {
|
||||
export class TiledMapRenderer extends RenderableComponent {
|
||||
public tiledMap: TmxMap;
|
||||
public physicsLayer: Ref<number> = new Ref(1 << 0);
|
||||
/**
|
||||
* 如果空,所有层将被渲染
|
||||
*/
|
||||
public layerIndicesToRender: number[];
|
||||
private toContainer: boolean = false;
|
||||
|
||||
public get width() {
|
||||
return this.tiledMap.width * this.tiledMap.tileWidth;
|
||||
}
|
||||
|
||||
public get height() {
|
||||
return this.tiledMap.height * this.tiledMap.tileHeight;
|
||||
}
|
||||
|
||||
public collisionLayer: TmxLayer;
|
||||
public _shouldCreateColliders: boolean;
|
||||
public _colliders: Collider[];
|
||||
|
||||
constructor(tiledMap: TmxMap, collisionLayerName: string = null, shouldCreateColliders: boolean = true) {
|
||||
super();
|
||||
this.tiledMap = tiledMap;
|
||||
this._shouldCreateColliders = shouldCreateColliders;
|
||||
this.displayObject = new egret.DisplayObjectContainer();
|
||||
|
||||
if (collisionLayerName) {
|
||||
this.collisionLayer = tiledMap.tileLayers.find(layer => layer.name == collisionLayerName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将此组件设置为只渲染单层
|
||||
* @param layerName
|
||||
*/
|
||||
public setLayerToRender(layerName: string) {
|
||||
this.layerIndicesToRender = [];
|
||||
this.layerIndicesToRender[0] = this.getLayerIndex(layerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置该组件应该按名称呈现哪些层。如果你知道索引,你可以直接设置layerIndicesToRender
|
||||
* @param layerNames
|
||||
*/
|
||||
public setLayersToRender(...layerNames: string[]) {
|
||||
this.layerIndicesToRender = [];
|
||||
for (let i = 0; i < layerNames.length; i++)
|
||||
this.layerIndicesToRender[i] = this.getLayerIndex(layerNames[i]);
|
||||
}
|
||||
|
||||
private getLayerIndex(layerName: string) {
|
||||
let index = 0;
|
||||
let layerType = this.tiledMap.getLayer(layerName);
|
||||
for (let layer in this.tiledMap.layers) {
|
||||
if (this.tiledMap.layers.hasOwnProperty(layer) &&
|
||||
this.tiledMap.layers[layer] == layerType) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public getRowAtWorldPosition(yPos: number): number {
|
||||
yPos -= this.entity.transform.position.y + this._localOffset.y;
|
||||
return this.tiledMap.worldToTilePositionY(yPos);
|
||||
}
|
||||
|
||||
public getColumnAtWorldPosition(xPos: number): number {
|
||||
xPos -= this.entity.transform.position.x + this._localOffset.x;
|
||||
return this.tiledMap.worldToTilePositionX(xPos);
|
||||
}
|
||||
|
||||
public onEntityTransformChanged(comp: transform.Component) {
|
||||
// 这里我们只处理位置变化。平铺地图不能缩放
|
||||
if (this._shouldCreateColliders && comp == transform.Component.position) {
|
||||
this.removeColliders();
|
||||
this.addColliders();
|
||||
}
|
||||
}
|
||||
|
||||
public onAddedToEntity() {
|
||||
this.addColliders();
|
||||
}
|
||||
|
||||
public onRemovedFromEntity() {
|
||||
this.removeColliders();
|
||||
}
|
||||
|
||||
public update() {
|
||||
this.tiledMap.update();
|
||||
}
|
||||
|
||||
public render(camera: es.Camera) {
|
||||
this.sync(camera);
|
||||
|
||||
if (!this.layerIndicesToRender) {
|
||||
TiledRendering.renderMap(this.tiledMap, !this.toContainer ? this.displayObject as egret.DisplayObjectContainer : null, Vector2.add(this.entity.transform.position, this._localOffset),
|
||||
this.transform.scale, this.renderLayer);
|
||||
} else {
|
||||
for (let i = 0; i < this.tiledMap.layers.length; i++) {
|
||||
if (this.tiledMap.layers[i].visible && this.layerIndicesToRender.contains(i))
|
||||
TiledRendering.renderLayerRenderCamera(this.tiledMap.layers[i] as TmxLayer, !this.toContainer ? this.displayObject as egret.DisplayObjectContainer : null, Vector2.add(this.entity.transform.position, this._localOffset),
|
||||
this.transform.scale, this.renderLayer, camera.bounds);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.toContainer){
|
||||
this.displayObject.cacheAsBitmap = true;
|
||||
this.toContainer = true;
|
||||
}
|
||||
}
|
||||
|
||||
public addColliders() {
|
||||
if (!this.collisionLayer || !this._shouldCreateColliders)
|
||||
return;
|
||||
|
||||
// 获取冲突层及其冲突的矩形
|
||||
let collisionRects = this.collisionLayer.getCollisionRectangles();
|
||||
|
||||
// 为我们收到的矩形创建碰撞器
|
||||
this._colliders = [];
|
||||
for (let i = 0; i < collisionRects.length; i++) {
|
||||
let collider = new BoxCollider(collisionRects[i].x + this._localOffset.x,
|
||||
collisionRects[i].y + this._localOffset.y, collisionRects[i].width, collisionRects[i].height);
|
||||
collider.physicsLayer = this.physicsLayer;
|
||||
collider.entity = this.entity;
|
||||
this._colliders[i] = collider;
|
||||
|
||||
Physics.addCollider(collider);
|
||||
}
|
||||
}
|
||||
|
||||
public removeColliders() {
|
||||
if (this._colliders == null)
|
||||
return;
|
||||
|
||||
for (let collider of this._colliders) {
|
||||
Physics.removeCollider(collider);
|
||||
}
|
||||
this._colliders = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
///<reference path="./SpriteRenderer.ts" />
|
||||
module es {
|
||||
import Bitmap = egret.Bitmap;
|
||||
import RenderTexture = egret.RenderTexture;
|
||||
|
||||
/**
|
||||
* 滚动由两张图片组合而成
|
||||
*/
|
||||
export class TiledSpriteRenderer extends SpriteRenderer {
|
||||
public get bounds(): Rectangle {
|
||||
if (this._areBoundsDirty){
|
||||
if (this._sprite){
|
||||
this._bounds.calculateBounds(this.entity.transform.position, this._localOffset, this._origin,
|
||||
this.entity.transform.scale, this.entity.transform.rotation, this.width, this.height);
|
||||
this._areBoundsDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
return this._bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纹理滚动的x值
|
||||
*/
|
||||
public get scrollX() {
|
||||
return this._sourceRect.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纹理滚动的x值
|
||||
* @param value
|
||||
*/
|
||||
public set scrollX(value: number) {
|
||||
this._sourceRect.x = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纹理滚动的y值
|
||||
*/
|
||||
public get scrollY() {
|
||||
return this._sourceRect.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纹理滚动的y值
|
||||
* @param value
|
||||
*/
|
||||
public set scrollY(value: number) {
|
||||
this._sourceRect.y = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纹理比例尺
|
||||
*/
|
||||
public get textureScale(): Vector2 {
|
||||
return this._textureScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纹理比例尺
|
||||
* @param value
|
||||
*/
|
||||
public set textureScale(value: Vector2) {
|
||||
this._textureScale = value;
|
||||
|
||||
// 重新计算我们的inverseTextureScale和源矩形大小
|
||||
this._inverseTexScale = new Vector2(1 / this._textureScale.x, 1 / this._textureScale.y);
|
||||
this._sourceRect.width = Math.floor(this._sprite.sourceRect.width * this._inverseTexScale.x);
|
||||
this._sourceRect.height = Math.floor(this._sprite.sourceRect.height * this._inverseTexScale.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆盖宽度值,这样TiledSprite可以有一个独立于其纹理的宽度
|
||||
*/
|
||||
public get width(): number{
|
||||
return this._sourceRect.width;
|
||||
}
|
||||
|
||||
public set width(value: number) {
|
||||
this._areBoundsDirty = true;
|
||||
this._sourceRect.width = value;
|
||||
}
|
||||
|
||||
public get height(): number {
|
||||
return this._sourceRect.height;
|
||||
}
|
||||
|
||||
public set height(value: number) {
|
||||
this._areBoundsDirty = true;
|
||||
this._sourceRect.height = value;
|
||||
}
|
||||
|
||||
public get gapXY(): Vector2{
|
||||
return new Vector2(this._gapX, this._gapY);
|
||||
}
|
||||
|
||||
public set gapXY(value: Vector2){
|
||||
this._gapX = value.x;
|
||||
this._gapY = value.y;
|
||||
|
||||
let renderTexture = new RenderTexture();
|
||||
let newRectangle = this.sprite.sourceRect;
|
||||
newRectangle.x = 0;
|
||||
newRectangle.y = 0;
|
||||
newRectangle.width += this._gapX;
|
||||
newRectangle.height += this._gapY;
|
||||
renderTexture.drawToTexture(this.displayObject, newRectangle);
|
||||
|
||||
if (!this.displayObject){
|
||||
this.displayObject = new Bitmap(renderTexture);
|
||||
}else{
|
||||
(this.displayObject as Bitmap).texture = renderTexture;
|
||||
}
|
||||
}
|
||||
|
||||
protected _sourceRect: Rectangle;
|
||||
protected _textureScale = Vector2.one;
|
||||
protected _inverseTexScale = Vector2.one;
|
||||
private _gapX = 0;
|
||||
private _gapY = 0;
|
||||
|
||||
constructor(sprite: Sprite) {
|
||||
super(sprite);
|
||||
|
||||
this._sourceRect = sprite.sourceRect;
|
||||
let bitmap = this.displayObject as Bitmap;
|
||||
bitmap.$fillMode = egret.BitmapFillMode.REPEAT;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置间隔
|
||||
* @param value
|
||||
*/
|
||||
public setGapXY(value: Vector2): TiledSpriteRenderer {
|
||||
this.gapXY = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public render(camera: es.Camera) {
|
||||
super.render(camera);
|
||||
|
||||
let bitmap = this.displayObject as Bitmap;
|
||||
bitmap.width = this.width;
|
||||
bitmap.height = this.height;
|
||||
bitmap.scrollRect = this._sourceRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user