Files
Monopoly/assets/app-bundle/app-view/page/main/native/PageMain.ts

679 lines
26 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { _decorator, Node, tween, Vec3, Sprite, Asset, ImageAsset, SpriteFrame, Texture2D, Label, UIOpacity, Animation, math, VideoPlayer, UITransform, VideoClip, Color, Button } from 'cc';
import BaseView from '../../../../../../extensions/app/assets/base/BaseView';
import { PageTips } from '../../tips/native/PageTips';
import { app } from 'db://assets/app/app';
import { Tools } from 'db://assets/res-native/tools/Tools';
import { PageRewardhistory } from '../../rewardhistory/native/PageRewardhistory';
import { USERDATA } from 'db://assets/res-native/data/UserData';
import { LANG_ENUM, LANG_SET, LangType } from 'db://assets/res-native/setting/LangSet';
const { ccclass, property } = _decorator;
/** 移动方向 */
const enum M_DIR {
CLOCKWISE = 0, /** 顺时针 */
COUNTER_CLOCKWISE = 1, /** 逆时针 */
}
const AUTO_TIMES : any[] = [
"∞",
"500",
"200",
"10",
]
@ccclass('PageMain')
export class PageMain extends BaseView {
/** 声音按钮 */
@property(Node) btn_music: Node = null!;
/** 当前余额 */
@property(Label) lab_gold: Label = null!;
/** 姓名 */
@property(Label) lab_name: Label = null!;
/** 中奖提示 */
@property(Label) lab_earn: Label = null!;
/** 倍率显示 */
@property(Label) lab_ante: Label = null!;
/** 方向选择 */
@property(Node) n_dir: Node[] = [];
/** 标签 */
@property(Node) sign: Node = null!;
/** 方块 */
@property(Node) blocks: Node[] = [];
/** 骰子 */
@property(Node) dices: Node[] = [];
/** 中奖 */
@property(Node) rw: Node = null!;
/** 未中奖 */
@property(Node) norw: Node = null!;
/** 开始游戏按钮 */
@property(Node) btn_play: Node = null!;
@property(Node) btn_auto: Node = null!;
/** 胜利数字 */
@property(Node) lab_add: Node = null!;
/** 失败数字 */
@property(Node) lab_sub: Node = null!;
/** 分数标签 */
@property(Node) lab_point: Node = null!;
/** 金币雨 */
@property(Node) gold_rain: Node = null!;
/** 背景视频 */
@property(VideoPlayer) video_bg: VideoPlayer = null!;
/** 视频背景图片 */
@property(Sprite) video_sprite: Sprite = null!;
/** 骰子动画 */
@property(Node) ani_dice: Node = null!;
/** 骰子总点数 */
@property(Node) dice_all: Node = null!;
/** 倍数减按钮 */
@property(Node) btn_ante_sub: Node = null!;
@property(Node) btn_ante_add: Node = null!;
/** 失败视频资源 */
@property(VideoClip) video_win: VideoClip = null!;
@property(VideoClip) video_lose: VideoClip = null!;
@property(VideoClip) video_normal: VideoClip = null!;
@property(Node) item_times: Node = null!;
@property(Node) list_times: Node = null!;
@property(Node) pnl_times: Node = null!;
/** 方块移动方向 */
private _move_dir: M_DIR = M_DIR.CLOCKWISE;
/** 是否正在游戏 */
private _is_playing = false;
/** 骰子数据 */
private _game_data: any = null;
/** 是否正在连续 */
private _is_continuous : boolean = false
/** 棋盘数据 */
private _board_data: any[] = []
/** 消耗倍数索引 */
private _ante_index : number = 0
/** 倍数数据 */
private _ante_data : any[] = null
/** 剩余次数 */
private _now_rest_times : number = 0
/** 标签位置 */
private _sign_position: Vec3[] =[
new Vec3(11, 491.7, 0),
new Vec3(178.2, 407, 0),
new Vec3(323.4, 310.2, 0),
new Vec3(456.5, 227.7, 0),
new Vec3(341, 143, 0),
new Vec3(233.2, 63.8, 0),
new Vec3(121, 0, 0),
new Vec3(231, -77, 0),
new Vec3(341, -143, 0),
new Vec3(467.5, -214.5, 0),
new Vec3(341, -321.2, 0),
new Vec3(231, -401.5, 0),
new Vec3(116.6, -473, 0),
new Vec3(0, -550, 0),
new Vec3(-117.7, -484, 0),
new Vec3(-234.3, -407, 0),
new Vec3(-344.3, -344.3, 0),
new Vec3(-465.3, -242, 0),
new Vec3(-336.6, -159.5, 0),
new Vec3(-226.6, -92.4, 0),
new Vec3(-115.5, -18.7, 0),
new Vec3(-227.7, 48.4, 0),
new Vec3(-341, 121, 0),
new Vec3(-451, 201.3, 0),
new Vec3(-301.4, 298.1, 0),
new Vec3(-159.5, 402.6, 0),
]
private _video:HTMLVideoElement = null
private _canvas:HTMLCanvasElement = null
private _ctx:CanvasRenderingContext2D = null
// 在类中新增变量用于缓存旧资源
private _currentSpriteFrame: SpriteFrame | null = null;
private _isUpdating = false;
// 初始化的相关逻辑写在这
onLoad() {}
// 界面打开时的相关逻辑写在这(onShow可被多次调用-它与onHide不成对)
onShow(params: any) {
this.initUserData()
this.initDir()
this.onClickTips()
this.bindVideoEvents()
this.getConfig()
this.initAutoTimes()
this._canvas = document.createElement('canvas');
this._canvas.width = this.video_sprite.getComponent(UITransform).width;
this._canvas.height = this.video_sprite.getComponent(UITransform).height;
this._canvas.style.display = 'none';
this._ctx = this._canvas.getContext('2d');
tween(this.ani_dice)
.repeatForever(
tween()
// 0.8秒内放大到1.2倍 + 顺时针旋转180度
.to(0.8, {
scale: new Vec3(1.2, 1.2, 1.2),
angle: 180 // 如果是2D节点用angle3D节点建议用 eulerAngles
}, { easing: 'sineOut' })
// 0.8秒内缩小回1倍 + 再旋转180度总共360度
.to(0.8, {
scale: new Vec3(1, 1, 1),
angle: 360
}, { easing: 'sineIn' })
// 完成一圈后,手动重置 angle 为 0防止数值无限叠加导致精度问题
.call(() => { this.ani_dice.angle = 0; })
)
.start()
}
// 界面关闭时的相关逻辑写在这(已经关闭的界面不会触发onHide)
onHide(result: undefined) {
// app.manager.ui.show<PageMain>({name: 'PageMain', onHide:(result) => { 接收到return的数据并且有类型提示 }})
return result;
}
initUserData(){
this.lab_gold.string = USERDATA.coin.toString()
this.lab_name.string = USERDATA.name
}
bindVideoEvents() {
if (this.video_bg) {
this.video_bg.node.on('ready-to-play', () => {
this._video = this.video_bg.nativeVideo
}, this);
}
}
/** 初始化方向选择 */
initDir(){
for (let i = 0; i < 2; i++) {
const element = this.n_dir[i];
Tools.ActChild(element, "light", i == this._move_dir)
Tools.ActChild(element, "dark", i != this._move_dir)
}
}
getConfig(){
Tools.httpReq("game/anteConfig", {}, (res:any) => {
this._ante_data = res
// 默认倍数
for (let index = 0; index < res.length; index++) {
const element = res[index];
if (element.is_default == 1){
this._ante_index = index
break
}
}
this.initAnteSet()
Tools.httpReq("game/lotteryPool", {}, (res:any) => {
this._board_data = res
this.initBoradData()
})
})
}
initAutoTimes(){
for (let i = 0; i < AUTO_TIMES.length; i++) {
const str = AUTO_TIMES[i]
let item = Tools.AddChild(this.list_times, this.item_times)
item.active = true
Tools.SetChildText(item, "lab", str)
Tools.SetTouchEndEvt(item, () => {
if (str == "∞"){
this._now_rest_times = -1
} else{
this._now_rest_times = parseInt(str)
}
this._is_continuous = true
this.hideSelectTimes()
Tools.ActChild(this.btn_auto, "lab", false)
this.btnGameStart()
})
}
}
hideSelectTimes(){
this.pnl_times.active = false
}
switchAuto(){
if (this._is_continuous){
this._is_continuous = false
this._now_rest_times = 0
Tools.ActChild(this.btn_auto, "lab", true)
Tools.SetChildText(this.btn_auto, "times", "")
}else {
if (this._is_playing) return
this.pnl_times.active = true
}
}
initAnteSet(){
let d = this._ante_data[this._ante_index]
this.lab_ante.string = d.name
this.btn_ante_sub.getComponent(Sprite).grayscale = this._ante_index == 0
this.btn_ante_add.getComponent(Sprite).grayscale = this._ante_index == this._ante_data.length - 1
}
btnAnteAdd(){
if (this._is_playing) return
if (this._ante_index < this._ante_data.length - 1) {
this._ante_index++
this.initAnteSet()
this.initBoradData()
}
}
btnAnteSub(){
if (this._is_playing) return
if (this._ante_index > 0) {
this._ante_index--
this.initAnteSet()
this.initBoradData()
}
}
initBoradData(){
for (let index = 0; index < this._board_data.length; index++) {
const d = this._board_data[index];
let num = parseFloat(d.ui_text) ? parseFloat(d.ui_text) * this._ante_data[this._ante_index].mult : d.ui_text
Tools.SetChildText(this.blocks[index], "num", num)
Tools.SetChildText(this.blocks[index], "light/num", num)
Tools.SetChildText(this.blocks[index], "point", d.grid_number)
Tools.SetChildText(this.blocks[index], "light/point", d.grid_number)
}
}
chooseDir(d:any, dir: number){
this._move_dir = dir
this.initDir()
}
/** 点击提示 */
onClickTips(){
app.manager.ui.show<PageTips>({name: 'PageTips'})
}
/** 点击音乐 */
oncClickMusic(){
let music = app.manager.sound.isMusicPlaying
music ? app.manager.sound.stopMusic() : app.manager.sound.playDefaultMusic()
this.loadRes(music ? "main_btn_sounds_off" : "main_btn_sounds_on", Asset, (res: ImageAsset)=>{
let sp = new SpriteFrame()
let tex = new Texture2D();
tex.image = res;
sp.texture = tex
this.btn_music.getComponent(Sprite).spriteFrame = sp
});
}
/** 点击历史 */
onClickRwHis(){
app.manager.ui.show<PageRewardhistory>({name: 'PageRewardhistory'})
}
/** 开始游戏 */
btnGameStart(){
if (this._is_playing) return
this._is_playing = true
this.lab_earn.string = ""
this.btn_ante_sub.getComponent(Sprite).grayscale = true
this.btn_ante_add.getComponent(Sprite).grayscale = true
this.btn_play.getComponent(Sprite).grayscale = true
if (this._is_continuous){
this._now_rest_times = this._now_rest_times == -1 ? -1 : this._now_rest_times - 1
Tools.SetChildText(this.btn_auto, "times", this._now_rest_times == -1 ? "∞" : this._now_rest_times.toString())
}
Tools.httpReq("game/playStart", {
"direction" : this._move_dir,
"ante" : this._ante_data[this._ante_index].mult
}, (res:any) => {
this._game_data = res
this._move_dir = res.direction
USERDATA.coin -= res.use_coin
this.initUserData()
let steps = 0
for (let i = 0; i < res.roll_array.length; i++) {
const element = res.roll_array[i];
steps += element
}
let index:any = this._board_data.findIndex(v => v.grid_number == steps.toString())
let b = this.blocks[index]
// 计算移动路径
let path = this.calculatePath(index, steps);
// 播放骰子动画
this.playDiceAni(res.roll_array)
// 判断是否是豹子
let baozi = this._game_data.is_win == 1
Tools.ActChild(this.rw, "bg/bg_title_super", baozi)
Tools.ActChild(this.rw, "bg/bg_title", !baozi)
this.gold_rain.active = baozi
// 如果是豹子,展示豹子特效后停止继续
if (baozi){
this.scheduleOnce(() => {
this.resetStopGame()
app.manager.sound.playEffect({name : "effect/eff_unsheathe"})
for (let i = 0; i < this.dices.length; i++) {
let eff = this.dices[i].getChildByPath("dice/eff")
eff.active = true
eff.setPosition(new Vec3(-25, -40, 0))
tween(eff)
.to(0.5, { position: new Vec3(25, 45, 0) })
.call(() => {
this.scheduleOnce(() => {
eff.active = false
}, 0.2)
})
.start()
}
this.scheduleOnce(() => {
Tools.ActChild(this.rw, "bg/win_lab/en", LANG_SET.langIndex == LangType.EN)
Tools.ActChild(this.rw, "bg/win_lab/zh", LANG_SET.langIndex == LangType.ZH)
Tools.SetChildText(this.rw, "bg/win_lab/" + LANG_ENUM[LANG_SET.langIndex] + "/num", this._game_data.win_coin)
this.rw.active = true
app.manager.sound.playEffect({name : "effect/eff_gold_rain"})
app.manager.sound.playEffect({name : "effect/eff_bigwin"})
USERDATA.coin = this._game_data.coin
USERDATA.total_ticket_count = this._game_data.total_ticket_count
this._is_playing = false
this.initUserData()
}, 1)
}, 1.2)
}else {
// 骰子总数展示
this.scheduleOnce(() => {
this.dice_all.active = true
Tools.SetChildText(this.dice_all, "dice/lab", steps.toString())
}, 1.2)
this.scheduleOnce(() => {
this.dice_all.active = false
}, 2.4)
//亮灯
this.scheduleOnce(() => {
Tools.ActChild(b, "light", true)
// 设置初始位置
this.sign.setPosition(this._sign_position[index]);
this.sign.active = true;
}, 2.4);
let lastIndex = index
if (this._move_dir == M_DIR.CLOCKWISE) {
lastIndex = (lastIndex + steps) % this._sign_position.length;
} else {
lastIndex = (lastIndex - steps + this._sign_position.length) % this._sign_position.length;
}
// 移动标签
this.scheduleOnce(() => {
// 执行动画
this.animateSign(path, steps * 0.12, lastIndex);
Tools.ActChild(b, "light", false)
}, 3);
}
},()=>{
this._is_playing = false
this.resetStopGame()
})
}
/**
* 计算移动路径
* @param index 当前位置索引
* @param steps 移动步数
*/
private calculatePath(index:number, steps: number): Vec3[] {
let path: Vec3[] = [];
let currentIndex = index;
for (let i = 0; i < steps; i++) {
if (this._move_dir == M_DIR.CLOCKWISE) {
currentIndex = (currentIndex + 1) % this._sign_position.length;
} else {
currentIndex = (currentIndex - 1 + this._sign_position.length) % this._sign_position.length;
}
path.push(this._sign_position[currentIndex]);
}
return path;
}
/**
* 执行动画
* @param path 移动路径
* @param duration 总时间(秒)
* @param lastIndex 最后一个位置索引
*/
private animateSign(path: Vec3[], duration: number, lastIndex: number = 0) {
let totalTime = duration;
let segmentTime = totalTime / path.length;
let moveNext = (index: number) => {
let targetPos = path[index];
app.manager.sound.playEffect({name : "effect/eff_jump", volume: 0.5})
/** 跟随闪烁 */
this.scheduleOnce(() => {
let n = this._sign_position.findIndex(v => v == targetPos)
let light = this.blocks[n].getChildByName("light")
light.active = true
let opc = light.getComponent(UIOpacity)
opc.opacity = 0
tween(opc)
.to(0.12, { opacity: 255 }) // 渐隐
.to(0.12 / 2, { opacity: 0 }) // 渐显 tween(opc)
.call(() => {
light.active = false
opc.opacity = 255
})
.start();
}, 0.06 / 2);
tween(this.sign)
.to(segmentTime, { position: targetPos }, { easing: "linear" })
.call(() => {
if (index + 1 >= path.length) {
this.scheduleOnce(() => {
this.sign.active = false;
let b = this.blocks[lastIndex]
if (this._game_data.win_coin >= 0){
app.manager.sound.playEffect({name : "effect/eff_start"})
}else{
app.manager.sound.playEffect({name : "effect/eff_finish", volume: 0.5})
}
this.blinkLightWithTween(this.blocks[lastIndex].getChildByName("light"), 1, 2)
}, 0.5);
return;
}
this.scheduleOnce(() => {
moveNext(index + 1);
}, 0.06);
})
.start();
};
moveNext(0);
}
/** 闪烁动画
* @param node 节点
* @param duration 持续时间(秒)
* @param times 闪烁次数
*/
private blinkLightWithTween(node: Node, duration: number, times: number) {
let opc = node.getComponent(UIOpacity);
if (!opc) return;
node.active = true
/** 胜利失败动画 */
this.video_bg.clip = this._game_data.win_coin >= 0 ? this.video_win : this.video_lose
app.manager.sound.playEffect({name : this._game_data.win_coin >= 0 ? "effect/eff_happy" : "effect/eff_thunder", volume: 0.5})
this.scheduleOnce(() => {
this.video_bg.clip = this.video_normal
}, 3.2);
let earn_str = this._game_data.win_coin > 0 ? "+" + this._game_data.win_coin : this._game_data.win_coin
this.lab_earn.string = earn_str
this.lab_earn.color = this._game_data.win_coin > 0 ? new Color(255, 91, 69) : new Color(96, 255, 38)
let blinkCount = 0;
const blink = () => {
tween(opc)
.to(duration / 2, { opacity: 0 }) // 渐隐
.to(duration / 2, { opacity: 255 }) // 渐显
.call(() => {
blinkCount++;
if (blinkCount < times) {
blink(); // 重复执行
// 游戏结束
}else{
opc.opacity = 255
node.active = false
this._is_playing = false
if (this._is_continuous){
let lab = Tools.AddChild(this.lab_point, this._game_data.win_coin > 0 ? this.lab_add : this.lab_sub)
let cb = () => {
lab.destroy()
USERDATA.coin = this._game_data.coin
this.initUserData()
if (this._now_rest_times > 0 || this._now_rest_times == -1){
this.btnGameStart()
}else{
this.resetStopGame()
}
}
// T5是再来一次不需要飘字
if (this._game_data.tier != "T5"){
let pos = new Vec3(node.parent.position.x, node.parent.position.y + 80, 0 )
lab.setPosition(pos)
lab.active = true
let earn_str = this._game_data.win_coin > 0 ? "+" + this._game_data.win_coin : this._game_data.win_coin
Tools.SetText(lab, earn_str)
app.manager.sound.playEffect({name : this._game_data.win_coin > 0 ? "effect/eff_win" : "effect/eff_lose"})
tween(lab)
.by(1, { position: new Vec3(0, 200, 0) })
.call(()=>{
cb()
})
.start()
}else {
cb()
}
}else {
if (this._game_data.tier != "T5"){
if (this._game_data.win_coin > 0){
Tools.ActChild(this.rw, "bg/win_lab/en", LANG_SET.langIndex == LangType.EN)
Tools.ActChild(this.rw, "bg/win_lab/zh", LANG_SET.langIndex == LangType.ZH)
Tools.SetChildText(this.rw, "bg/win_lab/" + LANG_ENUM[LANG_SET.langIndex] + "/num", this._game_data.win_coin)
app.manager.sound.playEffect({name : this._game_data.win_coin > 0 ? "effect/eff_win" : "effect/eff_lose"})
this.rw.active = true
}else{
this.norw.active = true
}
}
USERDATA.coin = this._game_data.coin
this.resetStopGame()
this.initUserData()
}
}
})
.start();
};
blink();
}
resetStopGame(){
this._is_continuous = false
Tools.ActChild(this.btn_auto, "lab", true)
Tools.SetChildText(this.btn_auto, "times", "")
this.btn_play.getComponent(Sprite).grayscale = false
this.initAnteSet()
}
/** 播放骰子动画 */
playDiceAni(array:any[]){
app.manager.sound.playEffect({name : "effect/shaizi"})
for (let i = 0; i < this.dices.length; i++) {
let dice = this.dices[i]
let animation = dice.getComponent(Animation);
if (animation) {
this.scheduleOnce(() => {
animation.play();
}, math.random() * 0.1);
animation.once(Animation.EventType.FINISHED, () => {
this.loadSprite("dice_" + array[i], dice.getChildByName("dice"))
});
}
}
}
loadSprite(pic: string, node: Node){
this.loadRes(pic, Asset, (res: ImageAsset)=>{
let sp = new SpriteFrame()
let tex = new Texture2D();
tex.image = res;
sp.texture = tex
node.getComponent(Sprite).spriteFrame = sp
});
}
closeRw(){
this.rw.active = false
this.norw.active = false
}
update(){
if (this._video) {
this.updateTexture();
}
}
private async updateTexture() {
if (this._isUpdating) return;
this._isUpdating = true;
try {
this._ctx.save(); // 保存当前状态
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
// 绘制视频帧
this._ctx.drawImage(this._video, 0, 0, this._canvas.width, this._canvas.height);
this._ctx.restore(); // 恢复原始状态
const canvasData = this._canvas;
// 销毁旧纹理
if (this._currentSpriteFrame) {
const oldTexture = this._currentSpriteFrame.texture;
oldTexture?.destroy();
this._currentSpriteFrame.destroy();
}
const texture = new Texture2D();
texture.image = new ImageAsset(canvasData);
const spriteFrame = new SpriteFrame();
spriteFrame.texture = texture;
this.video_sprite.spriteFrame = spriteFrame;
this._currentSpriteFrame = spriteFrame;
} finally {
this._isUpdating = false;
}
}
}