卡牌游戏工具模块
脚本入口: eve.Card()
面向卡牌游戏(炉石 / MTGA / 杀戮尖塔风格)的通用 UI 工具集:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、敌方手牌背面与偷看、费用不足置灰,以及 UiCard 同款的可实时调节布局参数。参考 ycarowr/UiCard 的功能面实现,以 C++ 模块提供(布局/交互/渲染均在本模块内完成)。
基本用法
对象关系与调用时机
eve.Card() 是单例模块,持有全部状态:卡牌类型定义表(registerCardsFromJson)、一份牌库(newDeck)、若干手牌(newHand)、若干落牌区(newZone)、若干布局配置(newConfig)。new* 返回的脚本对象是**非拥有句柄**——底层 C++ 对象由模块持有,脚本持有引用即可跨帧使用(不要手动释放)。
update 逐手牌推进布局与拖拽状态机,并把交互写入事件队列:getEventType(i) 为 "click"、"drop" 或 "dropRejected",getEventCardId(i) / getEventZone(i) / getEventHand(i) / getEventReason(i) 给出对应字段;处理完用 clearEvents() 清空。拖拽命中落牌区后卡牌**仍留在手牌数组**,由脚本调用 hand.removeCard(card) 决定是否离手;不处理则自动归位。
目标导向指南
做一个炉石式手牌与出牌流程
建玩家手牌(setInteractive(true)),按 card.getCost() > mana 逐帧 setDisabled(true) 置灰;在事件循环里命中 "drop" 且 getEventZone == "play" 时检查费用、hand.removeCard 并扣法力,"discard" 时弃掉,"hand" 时不做任何事。
敌方手牌 / 偷看
hand.setFaceDown(true) 整手渲染为牌背;hand.setPeek(true) 时翻面显示正面。setInteractive(false) 防止拖走敌方牌。
实时调节布局(UiCard Configs 面板)
把 LayoutConfig 的 getter/setter 接到 ImGui 面板:ui.slider + ui.consumeChange + ui.getValue,每帧用 ui.setValue 同步。可调:spacing、arcHeight、rotationAngle、hoverRotation、hoverScale、hoverLift、hoverSpeed、motionSpeed、disabledAlpha、handY。
常见问题
- 卡牌文字:C++ 模块会使用 Graphics 当前字体绘制文字;未设置字体时静默跳过文字,只画色块/宝石。字体资源由宿主渲染初始化流程管理。
mouse.isDown(1)是左键;面板悬浮时先查ui.wantCaptureMouse(),避免把 UI 操作误当卡牌拖拽。- 布局为“中心锚定”的抛物线扇形(两端上翘),
rotationAngle控制两端最大旋转角;hoverRotation控制悬浮时是否转正。 Zone.acceptKinds通过addAcceptKind设置后会参与实际 Drop 判定,不匹配的卡牌不会产生 drop 事件。newCard(defId)返回的实例 id 形如defId#N(保证唯一);卡牌定义用getCardDefinition*系列读取。
API 快查
eve.Card():registerCardsFromJson、clearCardDefinitions、getCardDefinitionCount、hasCardDefinition、getCardDefinitionName/Kind/Cost/Attack/Health/TintR/TintG/TintBeve.Card()工厂:newConfig()、newCard(defId)、newDeck()、newZone(id, label, x, y, w, h)、newHand(cfg)eve.Card()状态:setConfig/getConfig、handCount/getHand/findHand、zoneCount/getZone、getDeck、drawCard(handOwner)eve.Card()每帧:update(dt, mx, my, down)、render(gfx)、renderDeck(gfx)eve.Card()事件:clearEvents、getEventCount、getEventType、getEventHand、getEventZone、getEventCardId、getEventReasonLayoutConfig:get/setCardW、get/setCardH、get/setSpacing、get/setHandX、get/setHandY、get/setArcHeight、get/setRotationAngle、get/setHoverRotation、get/setHoverScale、get/setHoverLift、get/setHoverSpeed、get/setMotionSpeed、get/setDisabledAlpha、get/setShowZones、get/setDeckX、get/setDeckY、get/setDragThresholdCardData:getId/setId、getName/setName、getKind/setKind、getCost/setCost、getAttack/setAttack、getHealth/setHealth、isFaceUp/setFaceUp、isDisabled/setDisabled、getState/setState、getTintR/G/B、setTint(r, g, b)、setArt/getArt、getX/getY/getW/getH/getScale/getAlpha、isHovered/isDragging、hit(px, py)、describe()Deck:push、draw、peek、count、isEmpty、clear、shuffle、getCardZone:getId/setId、getLabel/setLabel、getX/getY/getW/getH、setRect、getColorR/G/B、setColor(r, g, b)、getAlpha/setAlpha、isEnabled/setEnabled、addAcceptKind、clearAcceptKinds、accepts、contains、renderHand:getOwner/setOwner、getConfig/setConfig、isFaceDown/setFaceDown、isPeek/setPeek、isInteractive/setInteractive、addCard、removeCard、clear、count、getCard、findCard、pickCard、render
使用要点
eve.Card()、LayoutConfig、Hand、Deck、Zone、CardData实例都保存在全局或实体状态中,eve_init里只建一次,不要每帧重建。- 一个
Hand使用一份独立的LayoutConfig(如敌方手牌新建一份并调setHandY),不要共享后改字段。 hand.addCard会把牌从 0 缩放平滑生长到槽位,形成“出牌”动画;拖拽未落入任何落牌区时自动归位。
源码: src/modules/card/
示例: examples/cardgame/
运行: make run/<platform>-debug GAME=examples/cardgame