import { AppKernel } from '@scripts/app/AppKernel'; import { GAME_TABS } from '@scripts/define/Define'; import { IGameInfo } from '@scripts/store/gameStore/data'; import { setSpriteFrame } from '@scripts/utils/ResHelper'; import { addBtnClick, removeBtnClick, setAlpha } from '@scripts/utils/UIHelper'; import { _decorator, Button, CCBoolean, Color, Component, isValid, Label, Node, Size, Sprite, UITransform, Vec4 } from 'cc'; const { ccclass, property } = _decorator; @ccclass('GameItem') export class GameItem extends Component { @property(Sprite) gameIcon: Sprite; @property(Node) btnFavo: Node; @property(Sprite) favoFlag: Sprite; @property(Node) hotFlag: Node; @property(Node) newFlag: Node; @property(Label) lblGameName: Label; @property(CCBoolean) isPortrait: boolean; @property({ tooltip: '配合 sprite-cover 材质,图片居中等比铺满节点区域' }) coverIcon = false; private _data: IGameInfo; private _dataIdx: number; protected onLoad(): void { if (this.coverIcon) { this.node.on(Node.EventType.SIZE_CHANGED, this.updateIconCover, this); this.updateIconCover(); } addBtnClick(this.gameIcon.node, this.onClick, this); addBtnClick(this.btnFavo, this.onBtnFavoClick, this); } public async updateItem(data: IGameInfo, index: number) { this._data = data; this._dataIdx = index; if (!this.node.active) this.node.active = true; if (this.lblGameName) this.lblGameName.string = data.gameName; setAlpha(this.favoFlag, data.isFavorite); this.setGameFlag(data.tags.map(t => t.tagCode)); const url = this.isPortrait ? data.coverUrl?.Portrait : data.coverUrl?.Square; if (!url) { this.gameIcon.spriteFrame = null; return; } const tmpIdx = this._dataIdx; const res = await AppKernel.resources.loadRemoteSp(url); if (tmpIdx === this._dataIdx && this._data === data && isValid(this.node)) { this.gameIcon.spriteFrame = res; this.updateIconCover(); } } private setGameFlag(tags: string[]) { this.newFlag.active = false; this.hotFlag.active = false; if (tags.includes(GAME_TABS.Hot)) { this.hotFlag.active = true; return; } if (tags.includes(GAME_TABS.New)) { this.newFlag.active = true; } } private updateIconCover(): void { if (!this.coverIcon) return; const target = this.node.getComponent(UITransform); const icon = this.gameIcon; icon.sizeMode = Sprite.SizeMode.CUSTOM; icon.getComponent(UITransform).setContentSize(target.contentSize); const frame = icon.spriteFrame; if (!frame || target.width <= 0 || target.height <= 0) return; const { width, height } = frame.rect; if (width <= 0 || height <= 0) return; const scale = Math.max(target.width / width, target.height / height); const sx = target.width / (width * scale); const sy = target.height / (height * scale); const uv = frame.uv; const material = icon.getMaterialInstance(0); if (!material) return; const nodeInfo = material.getProperty('nodeInfo') as Vec4; material.setProperty('nodeInfo', new Vec4(target.width, target.height, nodeInfo?.z ?? 1, 0)); // Invert the frame UV basis: bottom-left -> bottom-right / top-left. // Round corners must follow the node, independently of the cover crop. const ux = uv[2] - uv[0]; const uy = uv[3] - uv[1]; const vx = uv[4] - uv[0]; const vy = uv[5] - uv[1]; const determinant = ux * vy - uy * vx; if (Math.abs(determinant) < 1e-12) return; material.setProperty('nodeUVRowX', new Vec4( vy / determinant, -vx / determinant, (vx * uv[1] - vy * uv[0]) / determinant, 0, )); material.setProperty('nodeUVRowY', new Vec4( -uy / determinant, ux / determinant, (uy * uv[0] - ux * uv[1]) / determinant, 0, )); material.setProperty('uvTransform', new Vec4( frame.rotated ? sy : sx, frame.rotated ? sx : sy, (uv[0] + uv[6]) / 2, (uv[1] + uv[7]) / 2, )); } protected onDestroy(): void { this.node.off(Node.EventType.SIZE_CHANGED, this.updateIconCover, this); } private onClick() { AppKernel.appLobby.openGame(this._data); // AppKernel.appLobby.openGame(this._gameConfig.id); } private async onBtnFavoClick() { const gameStore = AppKernel.stores.gameStore; if (gameStore.favoriting) return; const index = this._dataIdx; const data = this._data; const isFavorite = data.isFavorite; const game = isFavorite ? await gameStore.reqFavoRemove(data.gameID) : await gameStore.reqFavoAdd(data.gameID); if (!!game && this._dataIdx === index) { this.updateItem(game, index); } } }