推荐流直玩,关闭效果

This commit is contained in:
huanghaipeng 2026-03-09 13:57:56 +08:00
parent fcc2369e89
commit 498ab8d3bf
21 changed files with 9416 additions and 6473 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"ver": "1.3.2",
"uuid": "4eaf518b-35ec-4262-928d-4d497c3f2830",
"uuid": "6eabf805-275a-4c3f-84de-eed951841e3e",
"importer": "scene",
"asyncLoadAssets": false,
"autoReleaseAssets": false,

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,7 @@ export default class JiaZai extends cc.Component {
private static cachedPassCheckPrefab: cc.Prefab | null = null;
private passCheckNode: cc.Node | null = null;
private isGetSidebar: boolean = false;
@property(cc.EditBox)
@ -658,6 +659,8 @@ export default class JiaZai extends cc.Component {
// console.log("HomeScene广告预加载初始化失败");
// }
// });
// 上报游戏加载完成时机(推荐流直玩需要)
cc.fx.GameTool.reportLoadComplete();
}
// 适配pad
@ -2232,25 +2235,29 @@ export default class JiaZai extends cc.Component {
// 可以在这里处理关卡信息和 UID
}
} else if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) {
// 处理分享链接参数
//@ts-ignore
const launchOptions = tt.getLaunchOptionsSync();
const query = launchOptions.query;
if (query.level && query.uid) {
const level = parseInt(query.level, 10);
const uid = query.uid;
cc.fx.GameConfig.GM_INFO.otherUid = uid;
cc.fx.GameConfig.GM_INFO.otherLevel = level;
// 可以在这里处理关卡信息和 UID
let eventData = {
identity: "helper", //发起者为helped 帮助者为helper
helpedId: cc.fx.GameConfig.GM_INFO.otherUid, //被帮助者UID
level: level //被帮助关卡等级
const uid = query.uid.toString();
if (uid != cc.fx.GameConfig.GM_INFO.uid) {
cc.fx.GameConfig.GM_INFO.otherUid = uid;
cc.fx.GameConfig.GM_INFO.otherLevel = level;
let eventData = {
identity: "helper", //发起者为helped 帮助者为helper
helpedId: cc.fx.GameConfig.GM_INFO.otherUid, //被帮助者UID
level: level //被帮助关卡等级
}
cc.fx.GameTool.shushu_Track("stage_help", eventData); //帮助通关
this.openShare();
}
cc.fx.GameTool.shushu_Track("stage_help", eventData); //帮助通关
this.openShare();
} else {
cc.fx.GameConfig.GM_INFO.otherLevel = 0;
cc.fx.GameConfig.GM_INFO.otherUid = "";
else {
cc.fx.GameConfig.GM_INFO.otherLevel = 0;
cc.fx.GameConfig.GM_INFO.otherUid = "";
}
// 可以在这里处理关卡信息和 UID
}
}
}
@ -3590,11 +3597,13 @@ export default class JiaZai extends cc.Component {
this.node.getChildByName("ToSidebar").getChildByName("sidebarBtn_1").active = false;
this.node.getChildByName("ToSidebar").getChildByName("sidebarBtn_2").active = false;
if (isGet) {
if (isGet || this.isGetSidebar) {
this.node.getChildByName("ToSidebar").getChildByName("sidebarBtn_2").active = true;
this.node.getChildByName("ToSidebar").getChildByName("close_btn").active = false;
this.node.getChildByName("ToSidebar").getChildByName("close_btn").active = true;
this.isGetSidebar = true;
} else {
this.node.getChildByName("ToSidebar").getChildByName("sidebarBtn_1").active = true;
this.isGetSidebar = false;
}
}
@ -3675,6 +3684,8 @@ export default class JiaZai extends cc.Component {
// }
if (lastRewardDate == 1) {
MiniGameSdk.API.showToast('已领取过侧边栏奖励');
// 加一个把界面隐藏
this.onSidebarClose();
return;
}
@ -3705,7 +3716,10 @@ export default class JiaZai extends cc.Component {
// 显示奖励提示
MiniGameSdk.API.showToast(`侧边栏奖励!获得${rewardHammer}个锤子`, 3);
console.log(`获得侧边栏奖励: ${rewardHammer}个锤子`);
// 存储领取状态
cc.fx.StorageMessage.setStorage('sidebar_reward_date', 1);
this.initDouyinSidebar();
this.onSidebarClose();
@ -3731,6 +3745,14 @@ export default class JiaZai extends cc.Component {
MiniGameSdk.BytedanceSidebar.listenFromSidebar((fromSidebar: boolean) => {
if (fromSidebar) {
console.log('用户从侧边栏启动游戏');
// 检查玩家是否已经领取过奖励
const lastRewardDate = cc.fx.StorageMessage.getStorage('sidebar_reward_date');
if (lastRewardDate == 1) {
console.log('玩家已经领取过侧边栏奖励,不再显示领取界面');
return;
}
// this.handleSidebarReward();
this.toSidebarShow(true);
@ -3804,6 +3826,32 @@ export default class JiaZai extends cc.Component {
sidebarBtn.active = false;
}
dy_kefu.active = true;
// 检测推荐流直玩场景
const recommendFeedResult = cc.fx.GameTool.checkRecommendFeedLaunch();
console.log("推荐流直玩场景检测结果", recommendFeedResult);
if (recommendFeedResult && recommendFeedResult.isRecommendFeedLaunch) {
// 检查 tt.getRecommendFeedInfo 是否存在
//@ts-ignore
if (tt && tt.getRecommendFeedInfo) {
//@ts-ignore
tt.getRecommendFeedInfo({
success: (res) => {
console.log("推荐流信息:", res);
// 处理推荐流信息
},
fail: (err) => {
console.error("获取推荐流信息失败:", err);
}
});
} else {
console.log("tt.getRecommendFeedInfo 方法不可用,跳过获取推荐流信息");
}
}
// 初始化推荐流直玩监听器
cc.fx.GameTool.initFeedGameListeners();
} else {
sidebarBtn.active = false;
dy_kefu.active = false;

View File

@ -2063,14 +2063,16 @@ export namespace MiniGameSdk {
// 构建分享参数
const shareParams = {
title: '如果你突然打了个喷嚏,那一定是我在等你帮忙过关!', // 分享标题
path: `/pages/index/index?level=${level}&uid=${uid}`, // 分享路径,带上关卡信息和 UID
imageUrl: img // 分享图片链接
};
// 调用微信分享 API
if (cc.sys.platform === cc.sys.WECHAT_GAME) {
//@ts-ignore
wx.shareAppMessage(shareParams);
wx.shareAppMessage({
...shareParams,
path: `/pages/index/index?level=${level}&uid=${uid}`, // 分享路径,带上关卡信息和 UID
});
let eventData = {
identity: "helped", //发起者为helped 帮助者为helper
helpedId: cc.fx.GameConfig.GM_INFO.uid, //被帮助者uid
@ -2082,6 +2084,7 @@ export namespace MiniGameSdk {
// @ts-ignore
tt.shareAppMessage({
...shareParams,
query: `level=${level}&uid=${uid}`, // 抖音使用 query 字段传递参数
success: (res) => {
console.log('抖音分享成功mini', res);
let eventData = {

View File

@ -19,6 +19,7 @@ export default class Utils {
wx.login({
success(res) {
console.log("微信login成功");
let restart = 0;
//console.log(res.code);
if (res.code) {
// 标志位,用于记录 callBack 是否已经被调用

View File

@ -1432,6 +1432,9 @@ var GameTool = {
let isWeChat = typeof wx !== 'undefined' && wx !== null && cc.sys.platform === cc.sys.WECHAT_GAME;
console.log("获取小游戏版号", isBytedance, isWeChat);
if (isBytedance) {
//@ts-ignore
const systemInfo = tt.getSystemInfoSync();
console.log('系统信息:', systemInfo);
//@ts-ignore
const envInfo = tt.getEnvInfoSync();
const { envType, mpVersion } = envInfo.microapp;
@ -2404,5 +2407,143 @@ var GameTool = {
cc.director.loadScene("GameScene");
},
// 检测推荐流直玩场景
checkRecommendFeedLaunch() {
if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) {
//@ts-ignore
const launchOptions = tt.getLaunchOptionsSync();
const scene = launchOptions.scene;
const query = launchOptions.query;
// 检测是否为推荐流直玩场景scene后四位是3041
const isRecommendFeedLaunch = scene && typeof scene === 'string' && scene.slice(-4) === '3041';
if (isRecommendFeedLaunch) {
// 处理推荐流直玩参数
let feedGameScene, feedGameExtra, feedGameContentId, feedGameChannel;
// 尝试解析 query 参数
if (query) {
const queryKeys = Object.keys(query);
if (queryKeys.length > 0) {
for (const key of queryKeys) {
if (key.includes('feed_game_scene')) {
feedGameScene = query[key];
} else if (key.includes('feed_game_extra')) {
feedGameExtra = query[key];
} else if (key.includes('feed_game_content_id')) {
feedGameContentId = query[key];
} else if (key.includes('feed_game_channel')) {
feedGameChannel = query[key];
}
}
if (!feedGameScene) {
for (const key of queryKeys) {
const value = query[key];
if (typeof value === 'string' && value.includes('feed_game_scene')) {
try {
const decoded = decodeURIComponent(value);
const parsed = JSON.parse(decoded);
if (parsed.query) {
feedGameScene = parsed.query.feed_game_scene;
feedGameExtra = parsed.query.feed_game_extra;
feedGameContentId = parsed.query.feed_game_content_id;
feedGameChannel = parsed.query.feed_game_channel;
}
} catch (e) {
// 解析失败
}
}
}
}
}
}
return {
isRecommendFeedLaunch: true,
scene: scene,
query: query,
feedGameScene: feedGameScene,
feedGameExtra: feedGameExtra,
feedGameContentId: feedGameContentId,
feedGameChannel: feedGameChannel
};
}
}
return {
isRecommendFeedLaunch: false
};
},
// 上报游戏加载完成时机(推荐流直玩需要)
reportLoadComplete() {
if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) {
//@ts-ignore
if (tt && tt.reportScene) {
//@ts-ignore
tt.reportScene({
sceneId: 7001,
success: function (res) {
// 上报成功
},
fail: function (res) {
// 上报失败
}
});
}
}
},
// 初始化推荐流直玩监听器
initFeedGameListeners() {
if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) {
//@ts-ignore
const launchOptions = tt.getLaunchOptionsSync();
const scene = launchOptions.scene;
// 检测是否为推荐流直玩场景scene后四位是3041
const isRecommendFeedLaunch = scene && typeof scene === 'string' && scene.slice(-4) === '3041';
if (isRecommendFeedLaunch) {
// 上报预启动数据
this.reportFeedGameData('preboot');
let firstEnter = true;
// 启动监听
//@ts-ignore
if (tt && tt.feedGame && tt.feedGame.onFeedStatusChange) {
//@ts-ignore
tt.feedGame.onFeedStatusChange(({ type }) => {
// 用户首次从预览流进入游戏
if (firstEnter && type === 'feedEnter') {
firstEnter = false;
// 上报用户活跃数据
this.reportFeedGameData('Activity');
}
});
}
}
}
},
// 上报推荐流直玩相关数据
reportFeedGameData(type: string) {
if (cc.sys.platform === cc.sys.BYTEDANCE_GAME) {
//@ts-ignore
if (tt && tt.feedGame && tt.feedGame.reportData) {
//@ts-ignore
tt.feedGame.reportData(type, {
success: function (res) {
// 上报成功
},
fail: function (res) {
// 上报失败
}
});
}
}
},
};
export { GameTool };

View File

@ -0,0 +1,366 @@
/**
* Genie Effect
* 使 RenderTexture Mac
* Cocos Creator 2.4.15
*/
const { ccclass, property } = cc._decorator;
@ccclass
export default class GenieEffect extends cc.Component {
@property(cc.Material)
genieMaterial: cc.Material = null;
@property
duration: number = 0.4;
@property
bendStrength: number = 0.5;
private _renderTexture: cc.RenderTexture = null;
private _captureSprite: cc.Sprite = null;
private _isPlaying: boolean = false;
onLoad() {
// 材质可以通过编辑器设置或动态传入
}
/**
* RenderTexture
*/
captureNode(): cc.SpriteFrame {
const node = this.node;
const size = node.getContentSize();
// 创建 RenderTexture
const renderTexture = new cc.RenderTexture();
renderTexture.initWithSize(size.width, size.height);
// 创建相机
const cameraNode = new cc.Node('CaptureCamera');
cameraNode.parent = node;
const camera = cameraNode.addComponent(cc.Camera);
// 设置相机
camera.targetTexture = renderTexture;
camera.clearFlags = cc.Camera.ClearFlags.COLOR | cc.Camera.ClearFlags.DEPTH;
camera.backgroundColor = cc.color(0, 0, 0, 0);
camera.zoomRatio = 1;
camera.cullingMask = 0xffffffff;
// 渲染一帧
camera.render();
// 销毁相机节点
cameraNode.destroy();
// 创建 SpriteFrame
const spriteFrame = new cc.SpriteFrame(renderTexture);
this._renderTexture = renderTexture;
return spriteFrame;
}
/**
* Sprite
*/
createCaptureSprite(): cc.Node {
// 捕获节点画面
const spriteFrame = this.captureNode();
// 创建 Sprite 节点
const spriteNode = new cc.Node('GenieCaptureSprite');
spriteNode.parent = this.node.parent;
spriteNode.setContentSize(this.node.getContentSize());
spriteNode.position = this.node.position;
spriteNode.anchorX = this.node.anchorX;
spriteNode.anchorY = this.node.anchorY;
// 添加 Sprite 组件
const sprite = spriteNode.addComponent(cc.Sprite);
sprite.spriteFrame = spriteFrame;
// 隐藏原节点
this.node.active = false;
this._captureSprite = sprite;
return spriteNode;
}
/**
*
* @param targetNode
* @param callback
*/
playClose(targetNode: cc.Node, callback?: Function) {
if (this._isPlaying) {
console.warn('GenieEffect: 动画正在播放中');
return;
}
if (!this.genieMaterial) {
console.warn('GenieEffect: 材质未加载,使用默认动画');
this.playDefaultClose(targetNode, callback);
return;
}
// 创建捕获的 Sprite 节点
const captureNode = this.createCaptureSprite();
if (!captureNode) {
console.warn('GenieEffect: 无法创建捕获节点');
this.playDefaultClose(targetNode, callback);
return;
}
this._isPlaying = true;
// 获取 Sprite 组件
const sprite = captureNode.getComponent(cc.Sprite);
// 获取起始位置和目标位置(世界坐标)
const startWorldPos = this.node.convertToWorldSpaceAR(cc.v2(0, 0));
const targetWorldPos = targetNode.convertToWorldSpaceAR(cc.v2(0, 0));
// 设置材质参数
const material = this.genieMaterial;
material.setProperty('u_targetPos', [targetWorldPos.x, targetWorldPos.y]);
material.setProperty('u_progress', 0);
material.setProperty('u_bendStrength', this.bendStrength);
material.setProperty('u_shrinkWidth', 0.2);
// 应用材质
sprite.setMaterial(0, material);
// 播放动画 - 同时移动节点和更新 Shader
const animData = { progress: 0 };
const startPos = captureNode.position;
const targetPos = captureNode.parent.convertToNodeSpaceAR(targetWorldPos);
cc.tween(animData)
.to(this.duration, { progress: 1 }, {
easing: 'quadInOut',
onUpdate: (target, ratio) => {
if (material && sprite) {
material.setProperty('u_progress', animData.progress);
}
// 同时移动捕获节点向目标点靠近
if (captureNode) {
const t = animData.progress;
// 使用缓动函数让移动更自然
const easeT = t * t * (3 - 2 * t); // smoothstep
captureNode.x = startPos.x + (targetPos.x - startPos.x) * easeT;
captureNode.y = startPos.y + (targetPos.y - startPos.y) * easeT;
}
}
})
.call(() => {
this._isPlaying = false;
// 销毁捕获节点
if (captureNode) {
captureNode.destroy();
}
if (callback) callback();
})
.start();
}
/**
*
* @param fromNode
* @param callback
*/
playOpen(fromNode: cc.Node, callback?: Function) {
if (this._isPlaying) {
console.warn('GenieEffect: 动画正在播放中');
return;
}
if (!this.genieMaterial) {
console.warn('GenieEffect: 材质未加载,使用默认动画');
this.playDefaultOpen(fromNode, callback);
return;
}
// 创建捕获的 Sprite 节点
const captureNode = this.createCaptureSprite();
if (!captureNode) {
console.warn('GenieEffect: 无法创建捕获节点');
this.playDefaultOpen(fromNode, callback);
return;
}
this._isPlaying = true;
// 获取 Sprite 组件
const sprite = captureNode.getComponent(cc.Sprite);
// 计算起始位置(世界坐标)
const fromWorldPos = fromNode.convertToWorldSpaceAR(cc.v2(0, 0));
// 设置材质参数(从完全吸入状态开始)
const material = this.genieMaterial;
material.setProperty('u_targetPos', [fromWorldPos.x, fromWorldPos.y]);
material.setProperty('u_progress', 1);
material.setProperty('u_bendStrength', this.bendStrength);
material.setProperty('u_shrinkWidth', 0.2);
// 应用材质
sprite.setMaterial(0, material);
// 播放动画progress 从 1 到 0
const animData = { progress: 1 };
cc.tween(animData)
.to(this.duration, { progress: 0 }, {
easing: 'quadOut',
onUpdate: (target, ratio) => {
if (material && sprite) {
material.setProperty('u_progress', animData.progress);
}
}
})
.call(() => {
this._isPlaying = false;
// 显示原节点
this.node.active = true;
// 销毁捕获节点
if (captureNode) {
captureNode.destroy();
}
if (callback) callback();
})
.start();
}
/**
* Shader 使
*/
private playDefaultClose(targetNode: cc.Node, callback?: Function) {
const startPos = this.node.position; const targetWorldPos = targetNode.convertToWorldSpaceAR(cc.v2(0, 0));
const targetLocalPos = this.node.parent.convertToNodeSpaceAR(targetWorldPos);
cc.tween(this.node)
.to(this.duration * 0.5, {
position: cc.v3(
startPos.x + (targetLocalPos.x - startPos.x) * 0.5,
startPos.y + (targetLocalPos.y - startPos.y) * 0.5,
0
),
scale: 0.6
}, { easing: 'quadIn' })
.to(this.duration * 0.5, {
position: cc.v3(targetLocalPos.x, targetLocalPos.y, 0),
scale: 0.01,
opacity: 0
}, { easing: 'expoIn' })
.call(() => {
if (callback) callback();
})
.start();
}
/**
* Shader 使
*/
private playDefaultOpen(fromNode: cc.Node, callback?: Function) {
const fromWorldPos = fromNode.convertToWorldSpaceAR(cc.v2(0, 0));
const fromLocalPos = this.node.parent.convertToNodeSpaceAR(fromWorldPos);
const endPos = this.node.position;
this.node.scale = 0.01;
this.node.opacity = 0;
this.node.position = cc.v3(fromLocalPos.x, fromLocalPos.y, 0);
cc.tween(this.node)
.to(this.duration * 0.5, {
position: cc.v3(
fromLocalPos.x + (endPos.x - fromLocalPos.x) * 0.5,
fromLocalPos.y + (endPos.y - fromLocalPos.y) * 0.5,
0
),
scale: 0.6,
opacity: 128
}, { easing: 'quadOut' })
.to(this.duration * 0.5, {
position: endPos,
scale: 1,
opacity: 255
}, { easing: 'expoOut' })
.call(() => {
if (callback) callback();
})
.start();
}
}
/**
* - 便
*/
export namespace GenieEffectUtil {
/**
*
* @param node
* @param targetNode
* @param callback
* @param material
* @param duration
*/
export function playClose(
node: cc.Node,
targetNode: cc.Node,
callback?: Function,
material?: cc.Material,
duration: number = 0.4
) {
const genieEffect = node.getComponent(GenieEffect);
if (genieEffect) {
genieEffect.playClose(targetNode, callback);
} else {
// 动态添加组件并播放
const effect = node.addComponent(GenieEffect);
effect.duration = duration;
if (material) {
effect.genieMaterial = material;
}
// 延迟一帧等待组件初始化
cc.director.once(cc.Director.EVENT_AFTER_UPDATE, () => {
effect.playClose(targetNode, callback);
});
}
}
/**
*
* @param node
* @param fromNode
* @param callback
* @param material
* @param duration
*/
export function playOpen(
node: cc.Node,
fromNode: cc.Node,
callback?: Function,
material?: cc.Material,
duration: number = 0.4
) {
const genieEffect = node.getComponent(GenieEffect);
if (genieEffect) {
genieEffect.playOpen(fromNode, callback);
} else {
const effect = node.addComponent(GenieEffect);
effect.duration = duration;
if (material) {
effect.genieMaterial = material;
}
cc.director.once(cc.Director.EVENT_AFTER_UPDATE, () => {
effect.playOpen(fromNode, callback);
});
}
}
}

View File

@ -0,0 +1,10 @@
{
"ver": "1.1.0",
"uuid": "72fc5a36-281e-44b0-be0a-419bb0276437",
"importer": "typescript",
"isPlugin": false,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,
"subMetas": {}
}

View File

@ -7,6 +7,7 @@
import JiaZai from "./JiaZai";
import Utils from "./module/Pay/Utils";
import GenieEffect, { GenieEffectUtil } from "./module/Tool/GenieEffect";
import NumberToImage from "./NumberToImage";
import SceneManager from "./SceneManager";
import { MiniGameSdk } from "./Sdk/MiniGameSdk";
@ -32,6 +33,9 @@ export default class NewClass extends cc.Component {
public juwai = false;
btn_Touch: boolean = false;
@property(cc.Material)
genieMaterial: cc.Material = null;
// LIFE-CYCLE CALLBACKS:
private onShowListener: () => void;
private iosPrice: number = 0;
@ -283,11 +287,47 @@ export default class NewClass extends cc.Component {
jiazaiComp.onGames();
}
if (this.node) {
// if (this.node) {
// this.node.active = false;
// }
const topNode = jiazaiNode.getChildByName("Load").getChildByName("Top");
if (!topNode) {
console.warn("找不到 Top 节点,直接销毁通行证界面");
this.destroyMonthlyCard();
return;
}
const yicon = topNode.getChildByName("yicon");
console.log("退出月卡:", yicon);
if (yicon && this.node && cc.isValid(this.node)) {
// 隐藏第一个子节点(可能包含按钮)
// this.node.children[0].active = false;
// 禁止所有按钮操作 - 禁用整个节点的交互
// this.setButtonsEnabled(false);
// 使用 GenieEffectUtil 播放关闭动画
// 参数:动画节点, 目标节点, 回调, 材质(可选)
GenieEffectUtil.playClose(this.node, yicon, () => {
// 动画完成后执行销毁逻辑
this.destroyMonthlyCard();
}, this.genieMaterial);
} else {
// 如果找不到 yicon直接销毁
this.destroyMonthlyCard();
}
}
destroyMonthlyCard() {
if (this.node && cc.isValid(this.node)) {
// 恢复节点原始状态
this.node.scale = 1;
this.node.opacity = 255;
this.node.active = false;
}
}
buyProduct(event, customData) {
if (!this.btn_Touch) {
return;

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,8 @@
{
"ver": "1.1.0",
"uuid": "cfe3e40a-7813-4913-93b2-b9f4066f8296",
"uuid": "f3f0dece-8b63-488c-92b9-3a0a93554ed0",
"importer": "javascript",
"isPlugin": false,
"isPlugin": true,
"loadPluginInWeb": true,
"loadPluginInNative": true,
"loadPluginInEditor": false,

13
assets/effect/genie.meta Normal file
View File

@ -0,0 +1,13 @@
{
"ver": "1.1.3",
"uuid": "82766fb6-9f6e-4af6-8908-2f2b1f828580",
"importer": "folder",
"isBundle": false,
"bundleName": "",
"priority": 1,
"compressionType": {},
"optimizeHotUpdate": {},
"inlineSpriteFrames": {},
"isRemoteBundle": {},
"subMetas": {}
}

View File

@ -0,0 +1,93 @@
CCEffect %{
techniques:
- name: opaque
passes:
- vert: genie-vs:vs # 程序 genie-vs入口 vs
frag: genie-fs:fs # 程序 genie-fs入口 fs
blendState:
targets:
- blend: true
blendSrc: src_alpha
blendDst: one_minus_src_alpha
rasterizerState:
cullMode: none
properties:
u_targetPos: { value: [0, -300] }
u_progress: { value: 0.0, editor: { slide: true, range: [0, 1], step: 0.01 } }
u_bendStrength: { value: 1.0, editor: { slide: true, range: [0, 3], step: 0.1 } }
u_shrinkWidth: { value: 0.2, editor: { slide: true, range: [0, 1], step: 0.01 } }
}%
CCProgram genie-vs %{
precision highp float;
#include <cc-global>
#include <cc-local>
in vec3 a_position;
in vec2 a_uv0;
in vec4 a_color;
uniform MyConstants {
vec2 u_targetPos;
float u_progress;
float u_bendStrength;
float u_shrinkWidth;
};
out vec2 v_uv;
out vec4 v_color;
vec2 quadBezier(vec2 p0, vec2 p1, vec2 p2, float t) {
float u = 1.0 - t;
return u * u * p0 + 2.0 * u * t * p1 + t * t * p2;
}
vec4 vs() {
vec2 pos = a_position.xy;
v_uv = a_uv0;
v_color = a_color;
float p = clamp(u_progress, 0.0, 1.0);
float uvY = a_uv0.y;
float uvX = a_uv0.x;
vec2 topTarget = mix(pos, u_targetPos, p * 0.6);
vec2 bottomTarget = u_targetPos;
float bendDown = -80.0 * u_bendStrength * (1.0 - p);
vec2 bottomControl = mix(pos, u_targetPos, 0.5) + vec2(0.0, bendDown);
vec2 rowPos = quadBezier(topTarget, bottomControl, bottomTarget, uvY * p);
float widthScale = 1.0 - (1.0 - u_shrinkWidth) * p * uvY;
vec2 center = mix(topTarget, bottomTarget, uvY);
rowPos.x = center.x + (rowPos.x - center.x) * widthScale;
float bulge = -sin(uvY * 3.14159) * 30.0 * u_bendStrength * (1.0 - p);
rowPos.y += bulge * sin((uvX - 0.5) * 3.14159);
// 增强吸进效果
float depth = 1.0 - p * 0.8 * uvY;
rowPos = mix(rowPos, u_targetPos, (1.0 - depth) * uvY * 1.0);
// 最终阶段快速收缩到目标点
if (p > 0.8) {
float finalT = (p - 0.8) / 0.2;
rowPos = mix(rowPos, u_targetPos, finalT * 0.8);
}
return cc_matViewProj * vec4(rowPos, 0.0, 1.0);
}
}%
CCProgram genie-fs %{
precision highp float;
#include <texture>
in vec2 v_uv;
in vec4 v_color;
uniform sampler2D texture;
vec4 fs() {
return texture(texture, v_uv) * v_color;
}
}%

View File

@ -0,0 +1,18 @@
{
"ver": "1.0.27",
"uuid": "3ce2780e-89e8-4262-ba0f-80fdfd58a026",
"importer": "effect",
"compiledShaders": [
{
"glsl1": {
"vert": "\nprecision highp float;\nuniform mat4 cc_matViewProj;\nattribute vec3 a_position;\nattribute vec2 a_uv0;\nattribute vec4 a_color;\nuniform vec2 u_targetPos;\nuniform float u_progress;\nuniform float u_bendStrength;\nuniform float u_shrinkWidth;\nvarying vec2 v_uv;\nvarying vec4 v_color;\nvec2 quadBezier(vec2 p0, vec2 p1, vec2 p2, float t) {\n float u = 1.0 - t;\n return u * u * p0 + 2.0 * u * t * p1 + t * t * p2;\n}\nvec4 vs() {\n vec2 pos = a_position.xy;\n v_uv = a_uv0;\n v_color = a_color;\n float p = clamp(u_progress, 0.0, 1.0);\n float uvY = a_uv0.y;\n float uvX = a_uv0.x;\n vec2 topTarget = mix(pos, u_targetPos, p * 0.6);\n vec2 bottomTarget = u_targetPos;\n float bendDown = -80.0 * u_bendStrength * (1.0 - p);\n vec2 bottomControl = mix(pos, u_targetPos, 0.5) + vec2(0.0, bendDown);\n vec2 rowPos = quadBezier(topTarget, bottomControl, bottomTarget, uvY * p);\n float widthScale = 1.0 - (1.0 - u_shrinkWidth) * p * uvY;\n vec2 center = mix(topTarget, bottomTarget, uvY);\n rowPos.x = center.x + (rowPos.x - center.x) * widthScale;\n float bulge = -sin(uvY * 3.14159) * 30.0 * u_bendStrength * (1.0 - p);\n rowPos.y += bulge * sin((uvX - 0.5) * 3.14159);\n float depth = 1.0 - p * 0.8 * uvY;\n rowPos = mix(rowPos, u_targetPos, (1.0 - depth) * uvY * 1.0);\n if (p > 0.8) {\n float finalT = (p - 0.8) / 0.2;\n rowPos = mix(rowPos, u_targetPos, finalT * 0.8);\n }\n return cc_matViewProj * vec4(rowPos, 0.0, 1.0);\n}\nvoid main() { gl_Position = vs(); }",
"frag": "\nprecision highp float;\nvarying vec2 v_uv;\nvarying vec4 v_color;\nuniform sampler2D texture;\nvec4 fs() {\n return texture2D(texture, v_uv) * v_color;\n}\nvoid main() { gl_FragColor = fs(); }"
},
"glsl3": {
"vert": "\nprecision highp float;\nuniform CCGlobal {\n mat4 cc_matView;\n mat4 cc_matViewInv;\n mat4 cc_matProj;\n mat4 cc_matProjInv;\n mat4 cc_matViewProj;\n mat4 cc_matViewProjInv;\n vec4 cc_cameraPos;\n vec4 cc_time;\n mediump vec4 cc_screenSize;\n mediump vec4 cc_screenScale;\n};\nuniform CCLocal {\n mat4 cc_matWorld;\n mat4 cc_matWorldIT;\n};\nin vec3 a_position;\nin vec2 a_uv0;\nin vec4 a_color;\nuniform MyConstants {\n vec2 u_targetPos;\n float u_progress;\n float u_bendStrength;\n float u_shrinkWidth;\n};\nout vec2 v_uv;\nout vec4 v_color;\nvec2 quadBezier(vec2 p0, vec2 p1, vec2 p2, float t) {\n float u = 1.0 - t;\n return u * u * p0 + 2.0 * u * t * p1 + t * t * p2;\n}\nvec4 vs() {\n vec2 pos = a_position.xy;\n v_uv = a_uv0;\n v_color = a_color;\n float p = clamp(u_progress, 0.0, 1.0);\n float uvY = a_uv0.y;\n float uvX = a_uv0.x;\n vec2 topTarget = mix(pos, u_targetPos, p * 0.6);\n vec2 bottomTarget = u_targetPos;\n float bendDown = -80.0 * u_bendStrength * (1.0 - p);\n vec2 bottomControl = mix(pos, u_targetPos, 0.5) + vec2(0.0, bendDown);\n vec2 rowPos = quadBezier(topTarget, bottomControl, bottomTarget, uvY * p);\n float widthScale = 1.0 - (1.0 - u_shrinkWidth) * p * uvY;\n vec2 center = mix(topTarget, bottomTarget, uvY);\n rowPos.x = center.x + (rowPos.x - center.x) * widthScale;\n float bulge = -sin(uvY * 3.14159) * 30.0 * u_bendStrength * (1.0 - p);\n rowPos.y += bulge * sin((uvX - 0.5) * 3.14159);\n float depth = 1.0 - p * 0.8 * uvY;\n rowPos = mix(rowPos, u_targetPos, (1.0 - depth) * uvY * 1.0);\n if (p > 0.8) {\n float finalT = (p - 0.8) / 0.2;\n rowPos = mix(rowPos, u_targetPos, finalT * 0.8);\n }\n return cc_matViewProj * vec4(rowPos, 0.0, 1.0);\n}\nvoid main() { gl_Position = vs(); }",
"frag": "\nprecision highp float;\nin vec2 v_uv;\nin vec4 v_color;\nuniform sampler2D texture;\nvec4 fs() {\n return texture(texture, v_uv) * v_color;\n}\nout vec4 cc_FragColor;\nvoid main() { cc_FragColor = fs(); }"
}
}
],
"subMetas": {}
}

View File

@ -0,0 +1,15 @@
{
"__type__": "cc.Material",
"_name": "genie",
"_objFlags": 0,
"_native": "",
"_effectAsset": {
"__uuid__": "3ce2780e-89e8-4262-ba0f-80fdfd58a026"
},
"_techniqueIndex": 0,
"_techniqueData": {
"0": {
"defines": {}
}
}
}

View File

@ -0,0 +1,7 @@
{
"ver": "1.0.5",
"uuid": "d53fa0ca-8b03-4458-8359-c0b3d5fd3821",
"importer": "material",
"dataAsSubAsset": null,
"subMetas": {}
}

View File

@ -3480,6 +3480,9 @@
"monthCardBtn2": {
"__id__": 34
},
"genieMaterial": {
"__uuid__": "d53fa0ca-8b03-4458-8359-c0b3d5fd3821"
},
"_id": ""
},
{

View File

@ -8753,6 +8753,9 @@
"__uuid__": "f0fbe7be-4949-478f-a6f5-06e86e7ffbdc"
},
"btnActivate1": null,
"genieMaterial": {
"__uuid__": "d53fa0ca-8b03-4458-8359-c0b3d5fd3821"
},
"_id": ""
},
{

View File

@ -2,6 +2,7 @@ import JiaZai from "../../Script/JiaZai";
import Utils from "../../Script/module/Pay/Utils";
import NumberToImage from "../../Script/NumberToImage";
import { MiniGameSdk } from "../../Script/Sdk/MiniGameSdk";
import { GenieEffectUtil } from "../../Script/module/Tool/GenieEffect";
const { ccclass, property } = cc._decorator;
@ -39,8 +40,8 @@ export default class passCheck extends cc.Component {
@property(cc.Prefab)
buyActivate: cc.Prefab = null;
@property(cc.Button)
btnActivate1: cc.Button = null;
@property(cc.Material)
genieMaterial: cc.Material = null;
private timeNumber: number = 0;
private progressNum1: number = 0;
@ -111,9 +112,9 @@ export default class passCheck extends cc.Component {
if (cc.fx.GameConfig.GM_INFO.getItemType == 2) {
this.oldPassCheckInfo = passCheckInfo[1];
passCheck = passCheckInfo[2];
if (this.activateBtn && this.activateBtn.node) {
this.activateBtn.node.active = !passCheck.activate;
}
if (this.activateBtn && this.activateBtn.node) {
this.activateBtn.node.active = !passCheck.activate;
}
} else {
this.oldPassCheckInfo = passCheckInfo[2];
passCheck = passCheckInfo[1];
@ -396,12 +397,10 @@ export default class passCheck extends cc.Component {
try {
// 先移除事件监听器
if (cc.sys.platform === cc.sys.WECHAT_GAME && this.onShowListener) {
console.log("shop移除onshow");
//@ts-ignore
wx.offShow(this.onShowListener);
this.onShowListener = null;
} else if (cc.sys.platform === cc.sys.BYTEDANCE_GAME && this.onShowListener) {
console.log("shop移除onshow");
//@ts-ignore
tt.offShow(this.onShowListener);
this.onShowListener = null;
@ -426,14 +425,73 @@ export default class passCheck extends cc.Component {
this.countdownTimer = null;
}
// 执行完整的销毁逻辑
this.destroyPassCheck();
// 执行关闭动画
this.playCloseAnimation();
} catch (error) {
console.error("关闭通行证界面时发生错误:", error);
// 即使出错也要确保清理引用
this.cleanupAndDestroy();
}
}
/**
* HomeScene passBtn
*/
private playCloseAnimation() {
// 找到 HomeScene 中的 passBtn
const jiazaiNode = cc.find("Canvas");
if (!jiazaiNode) {
console.warn("找不到 Canvas 节点,直接销毁通行证界面");
this.destroyPassCheck();
return;
}
const topNode = jiazaiNode.getChildByName("Load").getChildByName("Top");
if (!topNode) {
console.warn("找不到 Top 节点,直接销毁通行证界面");
this.destroyPassCheck();
return;
}
const passBtn = topNode.getChildByName("passBtn");
console.log("退出通行证:", passBtn);
if (passBtn && this.node && cc.isValid(this.node)) {
// 隐藏第一个子节点(可能包含按钮)
this.node.children[0].active = false;
// 禁止所有按钮操作 - 禁用整个节点的交互
this.setButtonsEnabled(false);
// 使用 GenieEffectUtil 播放关闭动画
GenieEffectUtil.playClose(this.node, passBtn, () => {
// 动画完成后执行销毁逻辑
this.destroyPassCheck();
}, this.genieMaterial);
} else {
// 如果找不到 passBtn直接销毁
this.destroyPassCheck();
}
}
/**
* /
* @param enabled true-false-
*/
private setButtonsEnabled(enabled: boolean) {
if (!this.node || !cc.isValid(this.node)) {
return;
}
// 遍历所有子节点,禁用/启用按钮
const buttons = this.node.getComponentsInChildren(cc.Button);
for (const button of buttons) {
button.enabled = enabled;
}
}
private destroyPassCheck() {
try {
// 先隐藏节点

View File

@ -2,7 +2,10 @@
"title": "ColorBlock",
"packageName": "org.cocos2d.demo",
"startScene": "ea96c58a-b731-4349-bcfc-c446571823ad",
"excludeScenes": [],
"excludeScenes": [
"0cb0fc28-9f05-4aff-878a-2414734c687c",
"0a811625-58f1-4abc-8ab1-2015263ba8e3"
],
"orientation": {
"landscapeRight": true,
"landscapeLeft": true,