123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- class TrailData {
- x: number = 0;
- y: number = 0;
- dis: number = 0;
- cos: number = 0;
- sin: number = 0;
- }
- import { CCInteger, CurveRange, Director, DynamicAtlasManager, GradientRange, IAssembler, RenderData, SpriteFrame, UIRenderer, UITransform, Vec2, Vec3, __private, _decorator, director, v2 } from 'cc';
- const { ccclass, property, executeInEditMode, menu } = _decorator;
- @ccclass
- @menu('Comp/MotionTrail')
- @executeInEditMode()
- export default class MotionTrail extends UIRenderer {
- @property({ visible: false })
- public get color() {
- return this._color
- }
- @property
- private _spriteFrame: SpriteFrame = null;
- @property({ type: SpriteFrame, displayName: 'SpriteFrame' })
- get spriteFrame() { return this._spriteFrame; }
- set spriteFrame(value: SpriteFrame) {
- this._spriteFrame = value;
- }
- @property
- private _isWorldXY: boolean = true;
- @property({ displayName: '世界坐标', tooltip: '顶点坐标是世界坐标还是本地坐标' })
- get isWorldXY() { return this._isWorldXY; }
- set isWorldXY(value: boolean) {
- this._isWorldXY = value;
- }
- @property({ displayName: '偏移' })
- offset: Vec2 = v2(0, 0);
- @property
- private _length: number = 20;
- @property({ type: CCInteger, displayName: '拖尾长度' })
- get length() { return this._length; }
- private set length(value: number) {
- this._length = Math.max(value, 0);
- this.updateLength();
- this.updateWidth();
- this.resetPos();
- this.freshBuffer()
- }
- trailData: TrailData[] = [];
- @property({ type: CurveRange, displayName: '宽度变化', range: [0, 1] })
- _sizeCurveRange: CurveRange = new CurveRange();
- @property({ type: CurveRange, displayName: '宽度变化', range: [0, 1] })
- get sizeCurveRange() {
- return this._sizeCurveRange;
- }
- set sizeCurveRange(value) {
- this._sizeCurveRange = value;
- this.updateWidth()
- }
- @property({ type: GradientRange, displayName: '颜色变化' })
- _colorCurveRange: GradientRange = new GradientRange();
- @property({ type: GradientRange, displayName: '颜色变化' })
- get colorCurveRange() {
- return this._colorCurveRange;
- }
- set colorCurveRange(value) {
- this._colorCurveRange = value;
- this._updateColor()
- }
- protected _render(render: __private._cocos_2d_renderer_i_batcher__IBatcher): void {
- render.commitComp(this, this.renderData, this._spriteFrame, this._assembler, null);
- }
- protected _flushAssembler(): void {
- this._assembler = simple
- if (!this._renderData) {
- this._renderData = this.requestRenderData();;
- this._renderData.material = this.getRenderMaterial(0)
- this.freshBuffer()
- this.markForUpdateRenderData()
- if (this._spriteFrame) {
- this._assembler.updateUVs(this)
- }
- this._updateColor()
- }
- }
- //更新buffer
- protected freshBuffer() {
- let len = this.length;
- let count = 4 + (len - 2) * 2
- this._renderData.dataLength = count;
- this._renderData.resize(count, (count - 2) * 3);
- }
- private updateLength() {
- let trailLen = this.length;
- let sub = trailLen - this.trailData.length;
- if (sub > 0) {
- for (let i = 0; i < sub; ++i) {
- this.trailData.push(new TrailData());
- }
- } else if (sub < 0) {
- this.trailData.splice(trailLen);
- }
- }
- private updateWidth() {
- let data = this.trailData;
- let trailLen = this.length;
- let width = this.getComponent(UITransform).width / 2
- for (let i = 0; i < trailLen; ++i) {
- data[i].dis = this.sizeCurveRange.evaluate(i / (trailLen - 1), 0) * width;
- }
- }
- private resetPos() {
- let data = this.trailData;
- let tx = this.offset.x;
- let ty = this.offset.y;
- if (this.isWorldXY) {
- tx += this.node.worldPosition.x;
- ty += this.node.worldPosition.y;
- } else {
- tx += this.node.position.x;
- ty += this.node.position.y;
- }
- for (let i = 0, len = this.length; i < len; i++) {
- data[i].x = tx;
- data[i].y = ty;
- }
- }
- onLoad(): void {
- this.length = this._length;
- }
- onEnable(): void {
- super.onEnable()
- director.once(Director.EVENT_BEFORE_DRAW, this.resetPos, this)
- }
- protected _useVertexOpacity: boolean = true
- }
- const simple: IAssembler = {
- updateRenderData(trail: MotionTrail) {
- const frame = trail.spriteFrame;
- DynamicAtlasManager.instance.packToDynamicAtlas(trail, frame);
- this.updateUVs(trail);// dirty need
- //this.updateColor(sprite);// dirty need
- const renderData = trail.renderData;
- if (renderData && frame) {
- if (renderData.vertDirty) {
- this.updateVertexData(trail);
- }
- renderData.updateRenderData(trail, frame);
- }
- },
- updateWorldVerts(trail: MotionTrail) {
- const renderData = trail.renderData;
- const vData = renderData.chunk.vb;
- let len = trail.length
- if (len < 1) return
- let tx = 0, ty = 0;
- let data = trail.trailData;
- if (!trail.isWorldXY) {
- tx = trail.node.position.x;
- ty = trail.node.position.y;
- }
- let ax = 0, ay = 0
- let curIdx = 0;
- let tempData: TrailData = null
- let tempNextData: TrailData = null
- //
- for (let i = 0; i < len; ++i) {
- tempData = data[i];
- let sameIdx = i
- for (; sameIdx < len; ++sameIdx) {
- tempNextData = data[sameIdx];
- if (tempNextData.x != tempData.x || tempNextData.y != tempData.y) {
- break
- }
- }
- sameIdx -= 1
- if (sameIdx == i) {
- ax = tempData.x - tx;
- ay = tempData.y - ty;
- vData[curIdx] = ax + tempData.dis * tempData.sin;
- vData[curIdx + 1] = ay - tempData.dis * tempData.cos;
- vData[curIdx + 2] = 0;
- curIdx += renderData.floatStride;
- vData[curIdx] = ax - tempData.dis * tempData.sin;
- vData[curIdx + 1] = ay + tempData.dis * tempData.cos;
- vData[curIdx + 2] = 0;
- curIdx += renderData.floatStride;
- } else {
- tempData = data[sameIdx];
- ax = tempData.x - tx;
- ay = tempData.y - ty;
- vData[curIdx] = ax + tempData.dis * tempData.sin;
- vData[curIdx + 1] = ay - tempData.dis * tempData.cos;
- vData[curIdx + 2] = 0;
- curIdx += renderData.floatStride;
- vData[curIdx] = ax - tempData.dis * tempData.sin;
- vData[curIdx + 1] = ay + tempData.dis * tempData.cos;
- vData[curIdx + 2] = 0;
- curIdx += renderData.floatStride;
- for (; i < sameIdx; ++i) {
- vData[curIdx] = vData[curIdx - renderData.floatStride * 2];
- vData[curIdx + 1] = vData[curIdx - renderData.floatStride * 2 + 1];
- vData[curIdx + 2] = 0;
- curIdx += renderData.floatStride;
- vData[curIdx] = vData[curIdx - renderData.floatStride * 2];
- vData[curIdx + 1] = vData[curIdx - renderData.floatStride * 2 + 1];
- vData[curIdx + 2] = 0;
- curIdx += renderData.floatStride;
- }
- }
- }
- },
- fillBuffers(trail: MotionTrail) {
- if (trail.spriteFrame == null) return
- const renderData = trail.renderData!;
- const chunk = renderData.chunk;
- this.update(trail)
- this.updateWorldVerts(trail, chunk);
- // quick version
- const vidOrigin = chunk.vertexOffset;
- const meshBuffer = chunk.meshBuffer;
- const ib = chunk.meshBuffer.iData;
- let indexOffset = meshBuffer.indexOffset;
- let vid = vidOrigin;
- let len = trail.length - 1
- for (let i = 0; i < len; i++) {
- ib[indexOffset++] = vid + 0;
- ib[indexOffset++] = vid + 1;
- ib[indexOffset++] = vid + 2;
- ib[indexOffset++] = vid + 1;
- ib[indexOffset++] = vid + 3;
- ib[indexOffset++] = vid + 2;
- vid += 2;
- }
- meshBuffer.indexOffset += len * 6;
- },
- updateVertexData(trail: MotionTrail) {
- const renderData: RenderData | null = trail.renderData;
- renderData.vertDirty = true
- },
- updateUVs(trail: MotionTrail) {
- if (trail.spriteFrame == null) return
- const renderData = trail.renderData;
- const vData = renderData.chunk.vb;
- const uv = trail.spriteFrame.uv;
- const length = trail.length - 1;
- const uvStep = length == 0 ? 0 : 1 / length;
- let curIdx = 3;
- let subV = uv[7] - uv[1]
- for (let i = 0; i < length; i++) {
- vData[curIdx] = uv[0];
- vData[curIdx + 1] = uv[1] + subV * i * uvStep;
- curIdx += renderData.floatStride
- vData[curIdx] = uv[0];
- vData[curIdx + 1] = uv[3] + subV * i * uvStep;
- curIdx += renderData.floatStride
- }
- vData[curIdx] = uv[0];
- vData[curIdx + 1] = uv[7];
- curIdx += renderData.floatStride
- vData[curIdx] = uv[0]
- vData[curIdx + 1] = uv[5];
- curIdx += renderData.floatStride
- },
- updateColor(trail: MotionTrail) {
- const renderData = trail.renderData!;
- const vData = renderData.chunk.vb;
- const total = 4 + (trail.length - 2) * 2;
- const stepTotal = trail.length
- let stepIdx = -1
- let colorOffset = 5;
- let curC, random
- for (let i = 0; i < total; i++) {
- if (i % 2 == 0) {
- random = (trail.colorCurveRange.mode === GradientRange.Mode.TwoGradients || trail.colorCurveRange.mode === GradientRange.Mode.TwoColors) ? Math.random() : 0;
- curC = trail.colorCurveRange.evaluate(stepIdx / stepTotal, random)
- stepIdx++
- }
- vData[colorOffset] = curC.r / 255;
- vData[colorOffset + 1] = curC.g / 255;
- vData[colorOffset + 2] = curC.b / 255;
- vData[colorOffset + 3] = curC.a / 255;
- colorOffset += renderData.floatStride;
- }
- },
- update(trail: MotionTrail) {
- if (trail.length === 0) return;
- let data = trail.trailData;
- for (let i = trail.length - 1; i > 0; --i) {
- let cur = data[i], prev = data[i - 1];
- cur.x = prev.x;
- cur.y = prev.y;
- cur.sin = prev.sin;
- cur.cos = prev.cos;
- }
- let pos: Vec3 = null
- if (trail.isWorldXY) {
- pos = trail.node.worldPosition;
- } else {
- pos = trail.node.position;
- }
- let fristData = data[0];
- fristData.x = trail.offset.x + pos.x;
- fristData.y = trail.offset.y + pos.y;
- let secondData = data[1];
- let radian = Math.atan2(secondData.y - fristData.y, secondData.x - fristData.x);
- fristData.sin = Math.sin(radian)
- fristData.cos = Math.cos(radian)
- },
- updateOpacity(trail: MotionTrail, alpha: number) {
- const renderData = trail.renderData!;
- const vData = renderData.chunk.vb;
- const total = 4 + (trail.length - 2) * 2;
- const stepTotal = trail.length
- let stepIdx = -1
- let colorOffset = 5;
- let curC, random
- for (let i = 0; i < total; i++) {
- if (i % 2 == 0) {
- random = (trail.colorCurveRange.mode === GradientRange.Mode.TwoGradients || trail.colorCurveRange.mode === GradientRange.Mode.TwoColors) ? Math.random() : 0;
- curC = trail.colorCurveRange.evaluate(stepIdx / stepTotal, random)
- stepIdx++
- }
- vData[colorOffset + 3] = curC.a / 255 * alpha;
- colorOffset += renderData.floatStride;
- }
- }
- }
|