diff --git a/assets/resources/effects/puzzle.effect b/assets/resources/effects/puzzle.effect new file mode 100644 index 0000000..d2c2e26 --- /dev/null +++ b/assets/resources/effects/puzzle.effect @@ -0,0 +1,138 @@ +// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd. +CCEffect %{ + techniques: + - passes: + - vert: sprite-vs:vert + frag: sprite-fs:frag + depthStencilState: + depthTest: false + depthWrite: false + blendState: + targets: + - blend: true + blendSrc: src_alpha + blendDst: one_minus_src_alpha + blendDstAlpha: one_minus_src_alpha + rasterizerState: + cullMode: none + properties: + rows: {value: 4} + columns: {value: 8} + sizeExpand: {value: 0.6} + radius: {value: 0.15} +}% + +CCProgram sprite-vs %{ + precision highp float; + #include + #if USE_LOCAL + #include + #endif + #if SAMPLE_FROM_RT + #include + #endif + in vec3 a_position; + in vec2 a_texCoord; + in vec4 a_color; + + out vec4 color; + out vec2 uv0; + + + vec4 vert () { + vec4 pos = vec4(a_position, 1); + + #if USE_LOCAL + pos = cc_matWorld * pos; + #endif + + #if USE_PIXEL_ALIGNMENT + pos = cc_matView * pos; + pos.xyz = floor(pos.xyz); + pos = cc_matProj * pos; + #else + pos = cc_matViewProj * pos; + #endif + + uv0 = a_texCoord; + #if SAMPLE_FROM_RT + CC_HANDLE_RT_SAMPLE_FLIP(uv0); + #endif + color = a_color; + + return pos; + } +}% + +CCProgram sprite-fs %{ + precision highp float; + #include + #include "../chunks/sdf2d" + uniform Const { + float rows; + float columns; + float sizeExpand; + float radius; + }; + in vec4 color; + in vec2 uv0; + + float smin(float a,float b,float k){ + float h = clamp(0.5+0.5*(a-b)/k,0.0,1.0); + return mix(a,b,h)-k*h*(1.0-h); + } + float smax(float a,float b,float k){ + return -smin(-a,-b,k); + } + float isOdd(float x) { + return fract(x/2.0) * 2.0; + } + #pragma builtin(local) + layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture; + + + vec4 frag () { + vec4 o = vec4(1, 1, 1, 1); + + vec2 id = floor(uv0 * vec2(columns, rows)); + vec2 uv_center = (id + 0.5) / vec2(columns, rows); + + float exp = sizeExpand + 1.0; + vec2 uv = (uv0 - uv_center) * vec2(columns, rows) * exp; + o *= CCSampleWithAlphaSeparated(cc_spriteTexture, (uv0 - uv_center) * exp + uv_center); + float d = sdBox(uv, vec2(0.5, 0.5)); + + float rad = radius; + float dd = 0.1; + float odd = isOdd(id.x + id.y); + float s = sign(odd - 0.5); + + float dc_top = sdCircle(uv + vec2(0, 0.5 - dd * s), rad); + float dc_bottom = sdCircle(uv + vec2(0, -0.5 + dd * s), rad); + float dc_left = sdCircle(uv + vec2(0.5 + dd * s, 0), rad); + float dc_right = sdCircle(uv + vec2(-0.5 - dd * s, 0), rad); + + float k = 0.02; + float nOdd = 1.0 - odd; + + float dt = smax(d, -dc_top, k) * odd + smin(d, dc_top , k) * nOdd; + float edge = step(id.y, 0.5); + d = dt * (1.0 - edge) + d * edge; + + float db = smax(d, -dc_bottom, k) * odd + smin(d, dc_bottom, k) * nOdd; + edge = step(rows - 1.5, id.y); + d = db * (1.0 - edge) + d * edge; + + float dl = smin(d, dc_left, k) * odd + smax(d, -dc_left, k) * nOdd; + edge = step(id.x, 0.5); + d = dl * (1.0 - edge) + d * edge; + + float dr = smin(d, dc_right, k) * odd + smax(d, -dc_right, k) * nOdd; + edge = step(columns - 1.5, id.x); + d = dr * (1.0 - edge) + d * edge; + + float c = smoothstep(0.01, -0.01, d); + o *= c; + return o; + } +}% diff --git a/assets/resources/effects/puzzle.effect.meta b/assets/resources/effects/puzzle.effect.meta new file mode 100644 index 0000000..a7e5cd9 --- /dev/null +++ b/assets/resources/effects/puzzle.effect.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.7.1", + "importer": "effect", + "imported": true, + "uuid": "8db6f7be-b91e-44ba-be47-5857f763dcc2", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/assets/resources/materials/puzzle.mtl b/assets/resources/materials/puzzle.mtl new file mode 100644 index 0000000..e2469f7 --- /dev/null +++ b/assets/resources/materials/puzzle.mtl @@ -0,0 +1,32 @@ +{ + "__type__": "cc.Material", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "_effectAsset": { + "__uuid__": "8db6f7be-b91e-44ba-be47-5857f763dcc2", + "__expectedType__": "cc.EffectAsset" + }, + "_techIdx": 0, + "_defines": [ + {} + ], + "_states": [ + { + "rasterizerState": {}, + "depthStencilState": {}, + "blendState": { + "targets": [ + {} + ] + } + } + ], + "_props": [ + { + "rows": 6, + "columns": 10 + } + ] +} \ No newline at end of file diff --git a/assets/resources/materials/puzzle.mtl.meta b/assets/resources/materials/puzzle.mtl.meta new file mode 100644 index 0000000..91a0c1a --- /dev/null +++ b/assets/resources/materials/puzzle.mtl.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.0.21", + "importer": "material", + "imported": true, + "uuid": "0080e1e8-609b-41f2-ad39-2a1c3fb9b4dc", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/assets/resources/scenes/puzzle.scene b/assets/resources/scenes/puzzle.scene new file mode 100644 index 0000000..a53c535 --- /dev/null +++ b/assets/resources/scenes/puzzle.scene @@ -0,0 +1,1196 @@ +[ + { + "__type__": "cc.SceneAsset", + "_name": "puzzle", + "_objFlags": 0, + "__editorExtras__": {}, + "_native": "", + "scene": { + "__id__": 1 + } + }, + { + "__type__": "cc.Scene", + "_name": "puzzle", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": null, + "_children": [ + { + "__id__": 2 + }, + { + "__id__": 5 + }, + { + "__id__": 7 + } + ], + "_active": true, + "_components": [], + "_prefab": { + "__id__": 29 + }, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "autoReleaseAssets": false, + "_globals": { + "__id__": 30 + }, + "_id": "946213b2-dc04-4756-8c82-e9df6ac4d571" + }, + { + "__type__": "cc.Node", + "_name": "Main Light", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 3 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": -0.06397656665577071, + "y": -0.44608233363525845, + "z": -0.8239028751062036, + "w": -0.3436591377065261 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": -117.894, + "y": -194.909, + "z": 38.562 + }, + "_id": "c0y6F5f+pAvI805TdmxIjx" + }, + { + "__type__": "cc.DirectionalLight", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 2 + }, + "_enabled": true, + "__prefab": null, + "_color": { + "__type__": "cc.Color", + "r": 255, + "g": 250, + "b": 240, + "a": 255 + }, + "_useColorTemperature": false, + "_colorTemperature": 6550, + "_staticSettings": { + "__id__": 4 + }, + "_visibility": -325058561, + "_illuminanceHDR": 65000, + "_illuminance": 65000, + "_illuminanceLDR": 1.6927083333333335, + "_shadowEnabled": false, + "_shadowPcf": 0, + "_shadowBias": 0.00001, + "_shadowNormalBias": 0, + "_shadowSaturation": 1, + "_shadowDistance": 50, + "_shadowInvisibleOcclusionRange": 200, + "_csmLevel": 4, + "_csmLayerLambda": 0.75, + "_csmOptimizationMode": 2, + "_csmAdvancedOptions": false, + "_csmLayersTransition": false, + "_csmTransitionRange": 0.05, + "_shadowFixedArea": false, + "_shadowNear": 0.1, + "_shadowFar": 10, + "_shadowOrthoSize": 5, + "_id": "597uMYCbhEtJQc0ffJlcgA" + }, + { + "__type__": "cc.StaticLightSettings", + "_baked": false, + "_editorOnly": false, + "_castShadow": false + }, + { + "__type__": "cc.Node", + "_name": "Main Camera", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 6 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": -10, + "y": 10, + "z": 10 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": -0.27781593346944056, + "y": -0.36497167621709875, + "z": -0.11507512748638377, + "w": 0.8811195706053617 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": -35, + "y": -45, + "z": 0 + }, + "_id": "c9DMICJLFO5IeO07EPon7U" + }, + { + "__type__": "cc.Camera", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 5 + }, + "_enabled": true, + "__prefab": null, + "_projection": 1, + "_priority": 0, + "_fov": 45, + "_fovAxis": 0, + "_orthoHeight": 10, + "_near": 1, + "_far": 1000, + "_color": { + "__type__": "cc.Color", + "r": 51, + "g": 51, + "b": 51, + "a": 255 + }, + "_depth": 1, + "_stencil": 0, + "_clearFlags": 14, + "_rect": { + "__type__": "cc.Rect", + "x": 0, + "y": 0, + "width": 1, + "height": 1 + }, + "_aperture": 19, + "_shutter": 7, + "_iso": 0, + "_screenScale": 1, + "_visibility": 1822425087, + "_targetTexture": null, + "_postProcess": null, + "_usePostProcess": false, + "_cameraType": -1, + "_trackingType": 0, + "_id": "7dWQTpwS5LrIHnc1zAPUtf" + }, + { + "__type__": "cc.Node", + "_name": "Canvas", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 1 + }, + "_children": [ + { + "__id__": 8 + }, + { + "__id__": 10 + }, + { + "__id__": 13 + }, + { + "__id__": 16 + }, + { + "__id__": 19 + }, + { + "__id__": 22 + } + ], + "_active": true, + "_components": [ + { + "__id__": 25 + }, + { + "__id__": 26 + }, + { + "__id__": 27 + }, + { + "__id__": 28 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 640, + "y": 360, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "97x1o6M2lMYLQLbwtQf9k8" + }, + { + "__type__": "cc.Node", + "_name": "Camera", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 7 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 9 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 1000 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 0, + "_layer": 1073741824, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "24+4NTAddAxYTJY/t3O3BE" + }, + { + "__type__": "cc.Camera", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 8 + }, + "_enabled": true, + "__prefab": null, + "_projection": 0, + "_priority": 1073741824, + "_fov": 45, + "_fovAxis": 0, + "_orthoHeight": 432.71729185727355, + "_near": 1, + "_far": 2000, + "_color": { + "__type__": "cc.Color", + "r": 0, + "g": 0, + "b": 0, + "a": 255 + }, + "_depth": 1, + "_stencil": 0, + "_clearFlags": 6, + "_rect": { + "__type__": "cc.Rect", + "x": 0, + "y": 0, + "width": 1, + "height": 1 + }, + "_aperture": 19, + "_shutter": 7, + "_iso": 0, + "_screenScale": 1, + "_visibility": 41943040, + "_targetTexture": null, + "_postProcess": null, + "_usePostProcess": false, + "_cameraType": -1, + "_trackingType": 0, + "_id": "57B0iKSkREnovF62D0ojB1" + }, + { + "__type__": "cc.Node", + "_name": "Node", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 7 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 11 + }, + { + "__id__": 12 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": -574.426, + "y": 184.065, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 1, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "a4UhIl+ANPCrVLy4jAYO6F" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 10 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 80, + "height": 80 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "16ZtVL9thH3ptNs4mwFhcu" + }, + { + "__type__": "f238cYbYUtBMZ1GtDGuhjN0", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 10 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": { + "__uuid__": "0080e1e8-609b-41f2-ad39-2a1c3fb9b4dc", + "__expectedType__": "cc.Material" + }, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 1, + "g": 1, + "b": 0, + "a": 255 + }, + "_sizeMode": 0, + "_atlas": null, + "_spriteFrame": { + "__uuid__": "10336bef-42f1-4bfd-a795-1f29554ccdd8@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_id": "deKgzPusdEg5ZS8uG08ftv" + }, + { + "__type__": "cc.Node", + "_name": "Node-001", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 7 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 14 + }, + { + "__id__": 15 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": -568.708, + "y": 69.123, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 1, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "deED1wRVtINILdl87JRfiA" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 13 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 80, + "height": 80 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "80XKSb+/1DTrOHHRR8Lq0L" + }, + { + "__type__": "f238cYbYUtBMZ1GtDGuhjN0", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 13 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": { + "__uuid__": "0080e1e8-609b-41f2-ad39-2a1c3fb9b4dc", + "__expectedType__": "cc.Material" + }, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 1, + "g": 1, + "b": 0, + "a": 255 + }, + "_sizeMode": 0, + "_atlas": null, + "_spriteFrame": { + "__uuid__": "10336bef-42f1-4bfd-a795-1f29554ccdd8@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_id": "6aY15ClFBEaZPjUXwBXEQB" + }, + { + "__type__": "cc.Node", + "_name": "Node-002", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 7 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 17 + }, + { + "__id__": 18 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": -588.795, + "y": -300.824, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 1, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "0dGjHm5x1Ix6BbOCCJxgBT" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 16 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 80, + "height": 80 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "63QY8BKIVMTpUv/Jspos/7" + }, + { + "__type__": "f238cYbYUtBMZ1GtDGuhjN0", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 16 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": { + "__uuid__": "0080e1e8-609b-41f2-ad39-2a1c3fb9b4dc", + "__expectedType__": "cc.Material" + }, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 1, + "g": 1, + "b": 0, + "a": 255 + }, + "_sizeMode": 0, + "_atlas": null, + "_spriteFrame": { + "__uuid__": "10336bef-42f1-4bfd-a795-1f29554ccdd8@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_id": "bbmQIYT7ZDV5uljzyjM4ge" + }, + { + "__type__": "cc.Node", + "_name": "Node-003", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 7 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 20 + }, + { + "__id__": 21 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": -482.73, + "y": 183.968, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 1, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "dbg3Ru1ldGqJCPO6uKwnPb" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 19 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 80, + "height": 80 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "49EcvY4QFPT6hF9yKtGXsl" + }, + { + "__type__": "f238cYbYUtBMZ1GtDGuhjN0", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 19 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": { + "__uuid__": "0080e1e8-609b-41f2-ad39-2a1c3fb9b4dc", + "__expectedType__": "cc.Material" + }, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 1, + "g": 1, + "b": 0, + "a": 255 + }, + "_sizeMode": 0, + "_atlas": null, + "_spriteFrame": { + "__uuid__": "10336bef-42f1-4bfd-a795-1f29554ccdd8@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_id": "a8ZTc35k1KkZtT4jc5ojTE" + }, + { + "__type__": "cc.Node", + "_name": "Node-004", + "_objFlags": 0, + "__editorExtras__": {}, + "_parent": { + "__id__": 7 + }, + "_children": [], + "_active": true, + "_components": [ + { + "__id__": 23 + }, + { + "__id__": 24 + } + ], + "_prefab": null, + "_lpos": { + "__type__": "cc.Vec3", + "x": -467.262, + "y": 72.039, + "z": 0 + }, + "_lrot": { + "__type__": "cc.Quat", + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "_lscale": { + "__type__": "cc.Vec3", + "x": 1, + "y": 1, + "z": 1 + }, + "_mobility": 1, + "_layer": 33554432, + "_euler": { + "__type__": "cc.Vec3", + "x": 0, + "y": 0, + "z": 0 + }, + "_id": "63i2nxA7tKCqmU3noOStnZ" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 22 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 80, + "height": 80 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "e36K2DSkNBGbGkFgZbDdx9" + }, + { + "__type__": "f238cYbYUtBMZ1GtDGuhjN0", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 22 + }, + "_enabled": true, + "__prefab": null, + "_customMaterial": { + "__uuid__": "0080e1e8-609b-41f2-ad39-2a1c3fb9b4dc", + "__expectedType__": "cc.Material" + }, + "_srcBlendFactor": 2, + "_dstBlendFactor": 4, + "_color": { + "__type__": "cc.Color", + "r": 1, + "g": 1, + "b": 0, + "a": 255 + }, + "_sizeMode": 0, + "_atlas": null, + "_spriteFrame": { + "__uuid__": "10336bef-42f1-4bfd-a795-1f29554ccdd8@f9941", + "__expectedType__": "cc.SpriteFrame" + }, + "_id": "1edDfLe8BDHrx2CuneIswT" + }, + { + "__type__": "cc.UITransform", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 7 + }, + "_enabled": true, + "__prefab": null, + "_contentSize": { + "__type__": "cc.Size", + "width": 1280, + "height": 720 + }, + "_anchorPoint": { + "__type__": "cc.Vec2", + "x": 0.5, + "y": 0.5 + }, + "_id": "84j8pOUeNERrDcw9M0JNri" + }, + { + "__type__": "cc.Canvas", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 7 + }, + "_enabled": true, + "__prefab": null, + "_cameraComponent": { + "__id__": 9 + }, + "_alignCanvasWithScreen": true, + "_id": "26UBuYJRpI9KIIdnTvWatb" + }, + { + "__type__": "cc.Widget", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 7 + }, + "_enabled": true, + "__prefab": null, + "_alignFlags": 45, + "_target": null, + "_left": 0, + "_right": 0, + "_top": 0, + "_bottom": 0, + "_horizontalCenter": 0, + "_verticalCenter": 0, + "_isAbsLeft": true, + "_isAbsRight": true, + "_isAbsTop": true, + "_isAbsBottom": true, + "_isAbsHorizontalCenter": true, + "_isAbsVerticalCenter": true, + "_originalWidth": 0, + "_originalHeight": 0, + "_alignMode": 2, + "_lockFlags": 0, + "_id": "d8fV9qDe1NMLo4ebtmoMFj" + }, + { + "__type__": "001cfOIS99BQ47PvZorYYlm", + "_name": "", + "_objFlags": 0, + "__editorExtras__": {}, + "node": { + "__id__": 7 + }, + "_enabled": true, + "__prefab": null, + "_id": "b115j7uvlPToVzvV8Vk/p+" + }, + { + "__type__": "cc.PrefabInfo", + "root": null, + "asset": null, + "fileId": "946213b2-dc04-4756-8c82-e9df6ac4d571", + "instance": null, + "targetOverrides": null + }, + { + "__type__": "cc.SceneGlobals", + "ambient": { + "__id__": 31 + }, + "shadows": { + "__id__": 32 + }, + "_skybox": { + "__id__": 33 + }, + "fog": { + "__id__": 34 + }, + "octree": { + "__id__": 35 + }, + "skin": { + "__id__": 36 + }, + "lightProbeInfo": { + "__id__": 37 + }, + "postSettings": { + "__id__": 38 + }, + "bakedWithStationaryMainLight": false, + "bakedWithHighpLightmap": false + }, + { + "__type__": "cc.AmbientInfo", + "_skyColorHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyColor": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.5, + "z": 0.8, + "w": 0.520833125 + }, + "_skyIllumHDR": 20000, + "_skyIllum": 20000, + "_groundAlbedoHDR": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_groundAlbedo": { + "__type__": "cc.Vec4", + "x": 0.2, + "y": 0.2, + "z": 0.2, + "w": 1 + }, + "_skyColorLDR": { + "__type__": "cc.Vec4", + "x": 0.452588, + "y": 0.607642, + "z": 0.755699, + "w": 0 + }, + "_skyIllumLDR": 0.8, + "_groundAlbedoLDR": { + "__type__": "cc.Vec4", + "x": 0.618555, + "y": 0.577848, + "z": 0.544564, + "w": 0 + } + }, + { + "__type__": "cc.ShadowsInfo", + "_enabled": false, + "_type": 0, + "_normal": { + "__type__": "cc.Vec3", + "x": 0, + "y": 1, + "z": 0 + }, + "_distance": 0, + "_planeBias": 1, + "_shadowColor": { + "__type__": "cc.Color", + "r": 76, + "g": 76, + "b": 76, + "a": 255 + }, + "_maxReceived": 4, + "_size": { + "__type__": "cc.Vec2", + "x": 1024, + "y": 1024 + } + }, + { + "__type__": "cc.SkyboxInfo", + "_envLightingType": 0, + "_envmapHDR": { + "__uuid__": "d032ac98-05e1-4090-88bb-eb640dcb5fc1@b47c0", + "__expectedType__": "cc.TextureCube" + }, + "_envmap": { + "__uuid__": "d032ac98-05e1-4090-88bb-eb640dcb5fc1@b47c0", + "__expectedType__": "cc.TextureCube" + }, + "_envmapLDR": { + "__uuid__": "6f01cf7f-81bf-4a7e-bd5d-0afc19696480@b47c0", + "__expectedType__": "cc.TextureCube" + }, + "_diffuseMapHDR": null, + "_diffuseMapLDR": null, + "_enabled": true, + "_useHDR": true, + "_editableMaterial": null, + "_reflectionHDR": null, + "_reflectionLDR": null, + "_rotationAngle": 0 + }, + { + "__type__": "cc.FogInfo", + "_type": 0, + "_fogColor": { + "__type__": "cc.Color", + "r": 200, + "g": 200, + "b": 200, + "a": 255 + }, + "_enabled": false, + "_fogDensity": 0.3, + "_fogStart": 0.5, + "_fogEnd": 300, + "_fogAtten": 5, + "_fogTop": 1.5, + "_fogRange": 1.2, + "_accurate": false + }, + { + "__type__": "cc.OctreeInfo", + "_enabled": false, + "_minPos": { + "__type__": "cc.Vec3", + "x": -1024, + "y": -1024, + "z": -1024 + }, + "_maxPos": { + "__type__": "cc.Vec3", + "x": 1024, + "y": 1024, + "z": 1024 + }, + "_depth": 8 + }, + { + "__type__": "cc.SkinInfo", + "_enabled": true, + "_blurRadius": 0.01, + "_sssIntensity": 3 + }, + { + "__type__": "cc.LightProbeInfo", + "_giScale": 1, + "_giSamples": 1024, + "_bounces": 2, + "_reduceRinging": 0, + "_showProbe": true, + "_showWireframe": true, + "_showConvex": false, + "_data": null, + "_lightProbeSphereVolume": 1 + }, + { + "__type__": "cc.PostSettingsInfo", + "_toneMappingType": 0 + } +] \ No newline at end of file diff --git a/assets/resources/scenes/puzzle.scene.meta b/assets/resources/scenes/puzzle.scene.meta new file mode 100644 index 0000000..5b4e666 --- /dev/null +++ b/assets/resources/scenes/puzzle.scene.meta @@ -0,0 +1,11 @@ +{ + "ver": "1.1.50", + "importer": "scene", + "imported": true, + "uuid": "946213b2-dc04-4756-8c82-e9df6ac4d571", + "files": [ + ".json" + ], + "subMetas": {}, + "userData": {} +} diff --git a/assets/resources/ui/images.meta b/assets/resources/ui/images.meta new file mode 100644 index 0000000..075929f --- /dev/null +++ b/assets/resources/ui/images.meta @@ -0,0 +1,9 @@ +{ + "ver": "1.2.0", + "importer": "directory", + "imported": true, + "uuid": "7a473951-f04b-4028-8ec6-741d8319818f", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/resources/ui/images/cyberpunk.jpeg b/assets/resources/ui/images/cyberpunk.jpeg new file mode 100644 index 0000000..bd0a735 Binary files /dev/null and b/assets/resources/ui/images/cyberpunk.jpeg differ diff --git a/assets/resources/ui/images/cyberpunk.jpeg.meta b/assets/resources/ui/images/cyberpunk.jpeg.meta new file mode 100644 index 0000000..b7b887a --- /dev/null +++ b/assets/resources/ui/images/cyberpunk.jpeg.meta @@ -0,0 +1,134 @@ +{ + "ver": "1.0.26", + "importer": "image", + "imported": true, + "uuid": "10336bef-42f1-4bfd-a795-1f29554ccdd8", + "files": [ + ".jpeg", + ".json" + ], + "subMetas": { + "6c48a": { + "importer": "texture", + "uuid": "10336bef-42f1-4bfd-a795-1f29554ccdd8@6c48a", + "displayName": "cyberpunk", + "id": "6c48a", + "name": "texture", + "userData": { + "wrapModeS": "clamp-to-edge", + "wrapModeT": "clamp-to-edge", + "minfilter": "linear", + "magfilter": "linear", + "mipfilter": "none", + "anisotropy": 0, + "isUuid": true, + "imageUuidOrDatabaseUri": "10336bef-42f1-4bfd-a795-1f29554ccdd8", + "visible": false + }, + "ver": "1.0.22", + "imported": true, + "files": [ + ".json" + ], + "subMetas": {} + }, + "f9941": { + "importer": "sprite-frame", + "uuid": "10336bef-42f1-4bfd-a795-1f29554ccdd8@f9941", + "displayName": "cyberpunk", + "id": "f9941", + "name": "spriteFrame", + "userData": { + "trimType": "auto", + "trimThreshold": 1, + "rotated": false, + "offsetX": 0, + "offsetY": 0, + "trimX": 0, + "trimY": 0, + "width": 800, + "height": 480, + "rawWidth": 800, + "rawHeight": 480, + "borderTop": 0, + "borderBottom": 0, + "borderLeft": 0, + "borderRight": 0, + "packable": false, + "pixelsToUnit": 100, + "pivotX": 0.5, + "pivotY": 0.5, + "meshType": 0, + "vertices": { + "rawPosition": [ + -400, + -240, + 0, + 400, + -240, + 0, + -400, + 240, + 0, + 400, + 240, + 0 + ], + "indexes": [ + 0, + 1, + 2, + 2, + 1, + 3 + ], + "uv": [ + 0, + 480, + 800, + 480, + 0, + 0, + 800, + 0 + ], + "nuv": [ + 0, + 0, + 1, + 0, + 0, + 1, + 1, + 1 + ], + "minPos": [ + -400, + -240, + 0 + ], + "maxPos": [ + 400, + 240, + 0 + ] + }, + "isUuid": true, + "imageUuidOrDatabaseUri": "10336bef-42f1-4bfd-a795-1f29554ccdd8@6c48a", + "atlasUuid": "" + }, + "ver": "1.0.12", + "imported": true, + "files": [ + ".json" + ], + "subMetas": {} + } + }, + "userData": { + "hasAlpha": false, + "type": "sprite-frame", + "fixAlphaTransparencyArtifacts": false, + "redirect": "10336bef-42f1-4bfd-a795-1f29554ccdd8@f9941" + } +} diff --git a/assets/scripts/components/ColorSDFAssembler.ts b/assets/scripts/components/ColorSDFAssembler.ts index 1c623d5..42a9634 100644 --- a/assets/scripts/components/ColorSDFAssembler.ts +++ b/assets/scripts/components/ColorSDFAssembler.ts @@ -59,11 +59,11 @@ export const ColorSDFAssembler: IAssembler = { const renderData = sprite.renderData!; const chunk = renderData.chunk; - if (sprite["_flagChangedVersion"] !== sprite.node["_flagChangedVersion"] || renderData.vertDirty) { + if (sprite["_flagChangedVersion"] !== sprite.node["flagChangedVersion"] || renderData.vertDirty) { // const vb = chunk.vertexAccessor.getVertexBuffer(chunk.bufferId); this.updateWorldVerts(sprite, chunk); renderData.vertDirty = false; - sprite["_flagChangedVersion"] = sprite.node["_flagChangedVersion"]; + sprite["_flagChangedVersion"] = sprite.node["flagChangedVersion"]; } // quick version @@ -158,6 +158,8 @@ export const ColorSDFAssembler: IAssembler = { updateColor (sprite: GPSpriteSDF) { const renderData = sprite.renderData!; const vData = renderData.chunk.vb; + if (!sprite.spriteFrame) + return const uv = sprite.spriteFrame.uv; let colorOffset = 5; diff --git a/assets/scripts/components/GPRoundBoxAssembler.ts b/assets/scripts/components/GPRoundBoxAssembler.ts index 6f4319d..981f237 100644 --- a/assets/scripts/components/GPRoundBoxAssembler.ts +++ b/assets/scripts/components/GPRoundBoxAssembler.ts @@ -107,6 +107,13 @@ export const GPRoundBoxAssembler: IAssembler = { this.updateWorldVerts(sprite, chunk); renderData.vertDirty = false; } + if (sprite["_flagChangedVersion"] !== sprite.node["flagChangedVersion"] || renderData.vertDirty) { + // const vb = chunk.vertexAccessor.getVertexBuffer(chunk.bufferId); + this.updateWorldVerts(sprite, chunk); + renderData.vertDirty = false; + sprite["_flagChangedVersion"] = sprite.node["flagChangedVersion"]; + } + // quick version const bid = chunk.bufferId; diff --git a/assets/scripts/puzzle/PuzzleAssembler.ts b/assets/scripts/puzzle/PuzzleAssembler.ts new file mode 100644 index 0000000..5d7aa31 --- /dev/null +++ b/assets/scripts/puzzle/PuzzleAssembler.ts @@ -0,0 +1,174 @@ +import { Color, IAssembler, IRenderData, RenderData, dynamicAtlasManager } from "cc"; +import { PuzzleSprite } from "./PuzzleSprite"; +const QUAD_INDICES = Uint16Array.from([0, 1, 2, 1, 3, 2]); + +export const PuzzleAssembler: IAssembler = { + createData (sprite: PuzzleSprite) { + const renderData = sprite.requestRenderData(); + renderData.dataLength = 4; + renderData.resize(4, 6); + renderData.chunk.setIndexBuffer(QUAD_INDICES); + return renderData; + }, + + updateRenderData (sprite: PuzzleSprite) { + const frame = sprite.spriteFrame; + + dynamicAtlasManager.packToDynamicAtlas(sprite, frame); + this.updateUVs(sprite);// dirty need + //this.updateColor(sprite);// dirty need + + const renderData = sprite.renderData; + if (renderData && frame) { + if (renderData.vertDirty) { + this.updateVertexData(sprite); + } + renderData.updateRenderData(sprite, frame); + } + }, + + updateWorldVerts (sprite: PuzzleSprite, chunk: { vb: any; }) { + const renderData = sprite.renderData!; + const vData = chunk.vb; + + const dataList: IRenderData[] = renderData.data; + const node = sprite.node; + const m = node.worldMatrix; + + const stride = renderData.floatStride; + let offset = 0; + const length = dataList.length; + for (let i = 0; i < length; i++) { + const curData = dataList[i]; + const x = curData.x; + const y = curData.y; + let rhw = m.m03 * x + m.m07 * y + m.m15; + rhw = rhw ? 1 / rhw : 1; + + offset = i * stride; + vData[offset + 0] = (m.m00 * x + m.m04 * y + m.m12) * rhw; + vData[offset + 1] = (m.m01 * x + m.m05 * y + m.m13) * rhw; + vData[offset + 2] = (m.m02 * x + m.m06 * y + m.m14) * rhw; + } + }, + + fillBuffers (sprite: PuzzleSprite) { + if (sprite === null) { + return; + } + + const renderData = sprite.renderData!; + const chunk = renderData.chunk; + + if (sprite["_flagChangedVersion"] !== sprite.node["flagChangedVersion"] || renderData.vertDirty) { + // const vb = chunk.vertexAccessor.getVertexBuffer(chunk.bufferId); + this.updateWorldVerts(sprite, chunk); + renderData.vertDirty = false; + sprite["_flagChangedVersion"] = sprite.node["flagChangedVersion"]; + } + + // quick version + const vidOrigin = chunk.vertexOffset; + const meshBuffer = chunk.meshBuffer; + const ib = chunk.meshBuffer.iData; + let indexOffset = meshBuffer.indexOffset; + + const vid = vidOrigin; + + // left bottom + ib[indexOffset++] = vid; + // right bottom + ib[indexOffset++] = vid + 1; + // left top + ib[indexOffset++] = vid + 2; + + // right bottom + ib[indexOffset++] = vid + 1; + // right top + ib[indexOffset++] = vid + 3; + // left top + ib[indexOffset++] = vid + 2; + + // IndexOffset should add 6 when vertices of a rect are visited. + meshBuffer.indexOffset += 6; + // slow version + // renderer.switchBufferAccessor().appendIndices(chunk); + }, + + updateVertexData (sprite: PuzzleSprite) { + const renderData: RenderData | null = sprite.renderData; + if (!renderData) { + return; + } + + const uiTrans = sprite.node._uiProps.uiTransformComp!; + const dataList: IRenderData[] = renderData.data; + const cw = uiTrans.width; + const ch = uiTrans.height; + const appX = uiTrans.anchorX * cw; + const appY = uiTrans.anchorY * ch; + let l = 0; + let b = 0; + let r = 0; + let t = 0; + + const frame = sprite.spriteFrame!; + const originSize = frame.originalSize; + const ow = originSize.width; + const oh = originSize.height; + const scaleX = cw / ow; + const scaleY = ch / oh; + const trimmedBorder = frame.trimmedBorder; + l = trimmedBorder.x * scaleX - appX; + b = trimmedBorder.z * scaleY - appY; + r = cw + trimmedBorder.y * scaleX - appX; + t = ch + trimmedBorder.w * scaleY - appY; + + + dataList[0].x = l; + dataList[0].y = b; + + dataList[1].x = r; + dataList[1].y = b; + + dataList[2].x = l; + dataList[2].y = t; + + dataList[3].x = r; + dataList[3].y = t; + + renderData.vertDirty = true; + }, + + updateUVs (sprite: PuzzleSprite) { + const renderData = sprite.renderData!; + const vData = renderData.chunk.vb; + const uv = sprite.uv; + // 01 11 00 10 + vData[3] = uv[0]; + vData[4] = uv[1]; + vData[12] = uv[2]; + vData[13] = uv[3]; + vData[21] = uv[4]; + vData[22] = uv[5]; + vData[30] = uv[6]; + vData[31] = uv[7]; + }, + + updateColor (sprite: PuzzleSprite) { + const renderData = sprite.renderData!; + const vData = renderData.chunk.vb; + let colorOffset = 5; + const color = sprite.color; + const colorR = color.r / 255; + const colorG = color.g / 255; + const colorB = color.b / 255; + const colorA = color.a / 255; + for (let i = 0; i < renderData.dataLength; i++, colorOffset += renderData.floatStride) { + vData[colorOffset] = colorR; + vData[colorOffset + 1] = colorG; + vData[colorOffset + 2] = colorB; + vData[colorOffset + 3] = colorA; + } + }, +}; \ No newline at end of file diff --git a/assets/scripts/puzzle/PuzzleAssembler.ts.meta b/assets/scripts/puzzle/PuzzleAssembler.ts.meta new file mode 100644 index 0000000..98ef777 --- /dev/null +++ b/assets/scripts/puzzle/PuzzleAssembler.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "a02a1645-241e-4702-988b-34876ae0ae10", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/scripts/puzzle/PuzzleSprite.ts b/assets/scripts/puzzle/PuzzleSprite.ts new file mode 100644 index 0000000..0e9710c --- /dev/null +++ b/assets/scripts/puzzle/PuzzleSprite.ts @@ -0,0 +1,310 @@ +import { _decorator, CCInteger, cclegacy, Color, Component, InstanceMaterialType, Material, Node, NodeEventType, RenderTexture, Sprite, SpriteAtlas, SpriteFrame, UIRenderer } from 'cc'; +import { BUILD, EDITOR } from 'cc/env'; +import { PuzzleAssembler } from './PuzzleAssembler'; +const { ccclass, property ,type, executionOrder} = _decorator; +enum EventType { + SPRITE_FRAME_CHANGED = 'spriteframe-changed', +} +@ccclass('PuzzleSprite') +export class PuzzleSprite extends UIRenderer { + // 尺寸模式,可以看枚举原本定义的地方有注释说明 + @property({serializable:true}) + protected _sizeMode = Sprite.SizeMode.TRIMMED; + @type(Sprite.SizeMode) + get sizeMode () { + return this._sizeMode; + } + set sizeMode (value) { + if (this._sizeMode === value) { + return; + } + + this._sizeMode = value; + if (value !== Sprite.SizeMode.CUSTOM) { + this._applySpriteSize(); + } + } + // 图集 + @property({serializable:true}) + protected _atlas: SpriteAtlas | null = null; + @type(SpriteAtlas) + get spriteAtlas () { + return this._atlas; + } + set spriteAtlas (value) { + if (this._atlas === value) { + return; + } + this._atlas = value; + } + + @property({serializable:true}) + protected _spriteFrame: SpriteFrame | null = null; + @type(SpriteFrame) + get spriteFrame () { + return this._spriteFrame; + } + set spriteFrame (value) { + if (this._spriteFrame === value) { + return; + } + + const lastSprite = this._spriteFrame; + this._spriteFrame = value; + this.markForUpdateRenderData(); + this._applySpriteFrame(lastSprite); + if (EDITOR) { + this.node.emit(EventType.SPRITE_FRAME_CHANGED, this); + } + } + + private _row =1 + @property({type:CCInteger, step:1, min:1}) + public get row(){return this._row} + public set row(v){this._row = v; + this._updateUVs() + } + + private _column = 1 + @property({type:CCInteger, step:1, min:1}) + public get column(){return this._column} + public set column(v){this._column = v; + this._updateUVs() + } + + private _rowMax = 4 + @property({type:CCInteger, step:1, min:1}) + public get rowMax(){return this._rowMax} + public set rowMax(v){this._rowMax = v; + this._updateUVs() + } + + private _columnMax = 4 + @property({type:CCInteger, step:1, min:1}) + public get columnMax(){return this._columnMax} + public set columnMax(v){this._columnMax = v; + this._updateUVs() + } + + public uv= [0,1, 1,1, 0,0, 1,0] + + onLoad(): void { + this._flushAssembler(); + } + + public __preload () { + this.changeMaterialForDefine(); + super.__preload(); + + if (EDITOR) { + this._resized(); + this.node.on(NodeEventType.SIZE_CHANGED, this._resized, this); + } + } + + public onEnable () { + console.log("onEnable") + super.onEnable(); + + // Force update uv, material define, active material, etc + this._activateMaterial(); + const spriteFrame = this._spriteFrame; + if (this.spriteFrame) + this._updateUVs(); + } + + public onDestroy () { + if (EDITOR) { + this.node.off(NodeEventType.SIZE_CHANGED, this._resized, this); + } + super.onDestroy(); + } + + /** + * @en + * Quickly switch to other sprite frame in the sprite atlas. + * If there is no atlas, the switch fails. + * + * @zh + * 选取使用精灵图集中的其他精灵。 + * @param name @en Name of the spriteFrame to switch. @zh 要切换的 spriteFrame 名字。 + */ + public changeSpriteFrameFromAtlas (name: string) { + if (!this._atlas) { + console.warn('SpriteAtlas is null.'); + return; + } + const sprite = this._atlas.getSpriteFrame(name); + this.spriteFrame = sprite; + } + + /** + * @deprecated Since v3.7.0, this is an engine private interface that will be removed in the future. + */ + public changeMaterialForDefine () { + let texture; + const lastInstanceMaterialType = this._instanceMaterialType; + if (this._spriteFrame) { + texture = this._spriteFrame.texture; + } + let value = false; + if (texture instanceof cclegacy.TextureBase) { + const format = texture.getPixelFormat(); + value = (format === cclegacy.TextureBase.PixelFormat.RGBA_ETC1 || format === cclegacy.TextureBase.PixelFormat.RGB_A_PVRTC_4BPPV1 || format === cclegacy.TextureBase.PixelFormat.RGB_A_PVRTC_2BPPV1); + } + + if (value) { + this._instanceMaterialType = InstanceMaterialType.USE_ALPHA_SEPARATED; + } else { + this._instanceMaterialType = InstanceMaterialType.ADD_COLOR_AND_TEXTURE; + } + if (lastInstanceMaterialType !== this._instanceMaterialType) { + // this.updateMaterial(); + // d.ts里没有注上这个函数,直接调用会表红。 + this["updateMaterial"](); + } + } + + protected _updateBuiltinMaterial () { + let mat = super._updateBuiltinMaterial(); + if (this.spriteFrame && this.spriteFrame.texture instanceof RenderTexture) { + const defines = { SAMPLE_FROM_RT: true, ...mat.passes[0].defines }; + const renderMat = new Material(); + renderMat.initialize({ + effectAsset: mat.effectAsset, + defines, + }); + mat = renderMat; + } + return mat; + } + + protected _render (render) { + render.commitComp(this, this.renderData, this._spriteFrame, this._assembler, null); + } + + protected _canRender () { + if (!super._canRender()) { + return false; + } + + const spriteFrame = this._spriteFrame; + if (!spriteFrame || !spriteFrame.texture) { + return false; + } + + return true; + } + + protected resetAssembler() { + this._assembler = null; + this._flushAssembler(); + } + protected _flushAssembler () { + const assembler = PuzzleAssembler; + + if (this._assembler !== assembler) { + this.destroyRenderData(); + this._assembler = assembler; + } + + + if (!this._renderData) { + if (this._assembler && this._assembler.createData) { + this._renderData = this._assembler.createData(this); + this._renderData!.material = this.getRenderMaterial(0); + this.markForUpdateRenderData(); + if (this.spriteFrame) { + this._assembler.updateRenderData(this); + } + this._updateColor(); + } + } + } + + private _applySpriteSize () { + if (this._spriteFrame) { + if (BUILD || !this._spriteFrame.isDefault) { + if (Sprite.SizeMode.RAW === this._sizeMode) { + const size = this._spriteFrame.originalSize; + this.node._uiProps.uiTransformComp!.setContentSize(size); + } else if (Sprite.SizeMode.TRIMMED === this._sizeMode) { + const rect = this._spriteFrame.rect; + this.node._uiProps.uiTransformComp!.setContentSize(rect.width, rect.height); + } + } + this.markForUpdateRenderData(true) + this._assembler.updateRenderData(this); + } + } + + private _resized () { + if (!EDITOR) { + return; + } + + if (this._spriteFrame) { + const actualSize = this.node._uiProps.uiTransformComp!.contentSize; + let expectedW = actualSize.width; + let expectedH = actualSize.height; + if (this._sizeMode === Sprite.SizeMode.RAW) { + const size = this._spriteFrame.originalSize; + expectedW = size.width; + expectedH = size.height; + } else if (this._sizeMode === Sprite.SizeMode.TRIMMED) { + const rect = this._spriteFrame.rect; + expectedW = rect.width; + expectedH = rect.height; + } + + if (expectedW !== actualSize.width || expectedH !== actualSize.height) { + this._sizeMode = Sprite.SizeMode.CUSTOM; + } + } + } + + private _activateMaterial () { + const spriteFrame = this._spriteFrame; + const material = this.getRenderMaterial(0); + if (spriteFrame) { + if (material) { + this.markForUpdateRenderData(); + } + } + + if (this.renderData) { + this.renderData.material = material; + } + } + + private _updateUVs () { + const _uv= [0,1, 1,1, 0,0, 1,0] + const blockWidth = 1 / this.columnMax + const blockHeight = 1 / this.rowMax + for (let i = 0; i < 4; i++) { + this.uv[i * 2] = (this.column - 1) * blockWidth + _uv[i * 2] * blockWidth; + this.uv[i * 2 + 1] = (this.row - 1) * blockHeight + _uv[i * 2 + 1] * blockHeight; + } + if (this._assembler) { + this._assembler.updateUVs(this); + } + } + + private _applySpriteFrame (oldFrame: SpriteFrame | null) { + const spriteFrame = this._spriteFrame; + + let textureChanged = false; + if (spriteFrame) { + if (!oldFrame || oldFrame.texture !== spriteFrame.texture) { + textureChanged = true; + } + if (textureChanged) { + if (this.renderData) this.renderData.textureDirty = true; + this.changeMaterialForDefine(); + } + this._applySpriteSize(); + } + } +} + + diff --git a/assets/scripts/puzzle/PuzzleSprite.ts.meta b/assets/scripts/puzzle/PuzzleSprite.ts.meta new file mode 100644 index 0000000..a8964b7 --- /dev/null +++ b/assets/scripts/puzzle/PuzzleSprite.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "f238c61b-614b-4131-9d46-b431ae863374", + "files": [], + "subMetas": {}, + "userData": {} +} diff --git a/assets/scripts/puzzle/PuzzleSpriteTest.ts b/assets/scripts/puzzle/PuzzleSpriteTest.ts new file mode 100644 index 0000000..2855e69 --- /dev/null +++ b/assets/scripts/puzzle/PuzzleSpriteTest.ts @@ -0,0 +1,274 @@ +import { _decorator, cclegacy, Component, InstanceMaterialType, Material, Node, NodeEventType, RenderTexture, Sprite, SpriteAtlas, SpriteFrame, UIRenderer } from 'cc'; +import { BUILD, EDITOR } from 'cc/env'; +import { PuzzleAssembler } from './PuzzleAssembler'; +import { ColorSDFAssembler } from '../components/ColorSDFAssembler'; +const { ccclass, property ,type} = _decorator; +enum EventType { + SPRITE_FRAME_CHANGED = 'spriteframe-changed', +} +@ccclass('PuzzleSpriteTest') +export class PuzzleSpriteTest extends UIRenderer { + // 尺寸模式,可以看枚举原本定义的地方有注释说明 + @property({serializable:true}) + protected _sizeMode = Sprite.SizeMode.TRIMMED; + @type(Sprite.SizeMode) + get sizeMode () { + return this._sizeMode; + } + set sizeMode (value) { + if (this._sizeMode === value) { + return; + } + + this._sizeMode = value; + if (value !== Sprite.SizeMode.CUSTOM) { + this._applySpriteSize(); + } + } + // 图集 + @property({serializable:true}) + protected _atlas: SpriteAtlas | null = null; + @type(SpriteAtlas) + get spriteAtlas () { + return this._atlas; + } + set spriteAtlas (value) { + if (this._atlas === value) { + return; + } + this._atlas = value; + } + + @property({serializable:true}) + protected _spriteFrame: SpriteFrame | null = null; + @type(SpriteFrame) + get spriteFrame () { + return this._spriteFrame; + } + set spriteFrame (value) { + if (this._spriteFrame === value) { + return; + } + + const lastSprite = this._spriteFrame; + this._spriteFrame = value; + this.markForUpdateRenderData(); + this._applySpriteFrame(lastSprite); + if (EDITOR) { + this.node.emit(EventType.SPRITE_FRAME_CHANGED, this); + } + } + + onLoad(): void { + this._flushAssembler(); + } + + public __preload () { + this.changeMaterialForDefine(); + super.__preload(); + + if (EDITOR) { + this._resized(); + this.node.on(NodeEventType.SIZE_CHANGED, this._resized, this); + } + } + + public onEnable () { + super.onEnable(); + + // Force update uv, material define, active material, etc + this._activateMaterial(); + const spriteFrame = this._spriteFrame; + if (spriteFrame) { + this._updateUVs(); + } + } + + public onDestroy () { + if (EDITOR) { + this.node.off(NodeEventType.SIZE_CHANGED, this._resized, this); + } + super.onDestroy(); + } + + /** + * @en + * Quickly switch to other sprite frame in the sprite atlas. + * If there is no atlas, the switch fails. + * + * @zh + * 选取使用精灵图集中的其他精灵。 + * @param name @en Name of the spriteFrame to switch. @zh 要切换的 spriteFrame 名字。 + */ + public changeSpriteFrameFromAtlas (name: string) { + if (!this._atlas) { + console.warn('SpriteAtlas is null.'); + return; + } + const sprite = this._atlas.getSpriteFrame(name); + this.spriteFrame = sprite; + } + + /** + * @deprecated Since v3.7.0, this is an engine private interface that will be removed in the future. + */ + public changeMaterialForDefine () { + let texture; + const lastInstanceMaterialType = this._instanceMaterialType; + if (this._spriteFrame) { + texture = this._spriteFrame.texture; + } + let value = false; + if (texture instanceof cclegacy.TextureBase) { + const format = texture.getPixelFormat(); + value = (format === cclegacy.TextureBase.PixelFormat.RGBA_ETC1 || format === cclegacy.TextureBase.PixelFormat.RGB_A_PVRTC_4BPPV1 || format === cclegacy.TextureBase.PixelFormat.RGB_A_PVRTC_2BPPV1); + } + + if (value) { + this._instanceMaterialType = InstanceMaterialType.USE_ALPHA_SEPARATED; + } else { + this._instanceMaterialType = InstanceMaterialType.ADD_COLOR_AND_TEXTURE; + } + if (lastInstanceMaterialType !== this._instanceMaterialType) { + // this.updateMaterial(); + // d.ts里没有注上这个函数,直接调用会表红。 + this["updateMaterial"](); + } + } + + protected _updateBuiltinMaterial () { + let mat = super._updateBuiltinMaterial(); + if (this.spriteFrame && this.spriteFrame.texture instanceof RenderTexture) { + const defines = { SAMPLE_FROM_RT: true, ...mat.passes[0].defines }; + const renderMat = new Material(); + renderMat.initialize({ + effectAsset: mat.effectAsset, + defines, + }); + mat = renderMat; + } + return mat; + } + + protected _render (render) { + render.commitComp(this, this.renderData, this._spriteFrame, this._assembler, null); + } + + protected _canRender () { + if (!super._canRender()) { + return false; + } + + const spriteFrame = this._spriteFrame; + if (!spriteFrame || !spriteFrame.texture) { + return false; + } + + return true; + } + + protected resetAssembler() { + this._assembler = null; + this._flushAssembler(); + } + protected _flushAssembler () { + const assembler = ColorSDFAssembler; + + if (this._assembler !== assembler) { + this.destroyRenderData(); + this._assembler = assembler; + } + + + if (!this._renderData) { + if (this._assembler && this._assembler.createData) { + this._renderData = this._assembler.createData(this); + this._renderData!.material = this.getRenderMaterial(0); + this.markForUpdateRenderData(); + if (this.spriteFrame) { + this._assembler.updateRenderData(this); + } + this._updateColor(); + } + } + } + + private _applySpriteSize () { + if (this._spriteFrame) { + if (BUILD || !this._spriteFrame.isDefault) { + if (Sprite.SizeMode.RAW === this._sizeMode) { + const size = this._spriteFrame.originalSize; + this.node._uiProps.uiTransformComp!.setContentSize(size); + } else if (Sprite.SizeMode.TRIMMED === this._sizeMode) { + const rect = this._spriteFrame.rect; + this.node._uiProps.uiTransformComp!.setContentSize(rect.width, rect.height); + } + } + this.markForUpdateRenderData(true) + this._assembler.updateRenderData(this); + } + } + + private _resized () { + if (!EDITOR) { + return; + } + + if (this._spriteFrame) { + const actualSize = this.node._uiProps.uiTransformComp!.contentSize; + let expectedW = actualSize.width; + let expectedH = actualSize.height; + if (this._sizeMode === Sprite.SizeMode.RAW) { + const size = this._spriteFrame.originalSize; + expectedW = size.width; + expectedH = size.height; + } else if (this._sizeMode === Sprite.SizeMode.TRIMMED) { + const rect = this._spriteFrame.rect; + expectedW = rect.width; + expectedH = rect.height; + } + + if (expectedW !== actualSize.width || expectedH !== actualSize.height) { + this._sizeMode = Sprite.SizeMode.CUSTOM; + } + } + } + + private _activateMaterial () { + const spriteFrame = this._spriteFrame; + const material = this.getRenderMaterial(0); + if (spriteFrame) { + if (material) { + this.markForUpdateRenderData(); + } + } + + if (this.renderData) { + this.renderData.material = material; + } + } + + private _updateUVs () { + if (this._assembler) { + this._assembler.updateUVs(this); + } + } + + private _applySpriteFrame (oldFrame: SpriteFrame | null) { + const spriteFrame = this._spriteFrame; + + let textureChanged = false; + if (spriteFrame) { + if (!oldFrame || oldFrame.texture !== spriteFrame.texture) { + textureChanged = true; + } + if (textureChanged) { + if (this.renderData) this.renderData.textureDirty = true; + this.changeMaterialForDefine(); + } + this._applySpriteSize(); + } + } +} + + diff --git a/assets/scripts/puzzle/PuzzleSpriteTest.ts.meta b/assets/scripts/puzzle/PuzzleSpriteTest.ts.meta new file mode 100644 index 0000000..b4910a5 --- /dev/null +++ b/assets/scripts/puzzle/PuzzleSpriteTest.ts.meta @@ -0,0 +1,9 @@ +{ + "ver": "4.0.23", + "importer": "typescript", + "imported": true, + "uuid": "bff05d5e-35b8-40ca-8ceb-939a046440e8", + "files": [], + "subMetas": {}, + "userData": {} +}