载入中...
搜索中...
未找到
卡牌游戏工具模块

卡牌游戏工具模块

脚本入口: eve.Card()

面向卡牌游戏(炉石 / MTGA / 杀戮尖塔风格)的通用 UI 工具集:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、敌方手牌背面与偷看、费用不足置灰,以及 UiCard 同款的可实时调节布局参数。参考 ycarowr/UiCard 的功能面实现,以 C++ 模块提供(布局/交互/渲染均在本模块内完成)。

基本用法

local card = eve.Card();
// 注册卡牌类型定义
card.registerCardsFromJson(@"[
{""id"":""flame"",""name"":""烈焰元素"",""kind"":""creature"",""cost"":2,""attack"":3,""health"":2,""tint"":[0.85,0.35,0.20]},
{""id"":""fireball"",""name"":""火球术"",""kind"":""spell"",""cost"":3,""tint"":[0.95,0.55,0.25]}
]");
// 布局配置(UiCard 的 Configs)
local cfg = card.newConfig();
cfg.setHandX(config.width * 0.5);
cfg.setHandY(config.height - 80.0);
card.setConfig(cfg);
// 牌库 + 手牌 + 落牌区
local deck = card.newDeck();
deck.push(card.newCard("flame"));
deck.shuffle();
local ph = card.newHand(cfg);
ph.setOwner("player");
local eh = card.newHand(card.newConfig());
eh.setOwner("enemy");
eh.setFaceDown(true); // 背面朝上
eh.setInteractive(false); // 不可拖拽
card.newZone("play", "出牌区", config.width * 0.5 - 240, config.height * 0.32, 480, 170);
card.drawCard("player"); // 从牌库抽一张到手牌
// 每帧
card.update(dt, mouse.getX(), mouse.getY(), mouse.isDown(1) && !ui.wantCaptureMouse());
card.render(gfx); // 画落牌区 + 所有手牌
card.renderDeck(gfx); // 画牌库堆 + 剩余张数

对象关系与调用时机

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 同步。可调:spacingarcHeightrotationAnglehoverRotationhoverScalehoverLifthoverSpeedmotionSpeeddisabledAlphahandY

常见问题

  • 卡牌文字:C++ 模块会使用 Graphics 当前字体绘制文字;未设置字体时静默跳过文字,只画色块/宝石。字体资源由宿主渲染初始化流程管理。
  • mouse.isDown(1) 是左键;面板悬浮时先查 ui.wantCaptureMouse(),避免把 UI 操作误当卡牌拖拽。
  • 布局为“中心锚定”的抛物线扇形(两端上翘),rotationAngle 控制两端最大旋转角;hoverRotation 控制悬浮时是否转正。
  • Zone.acceptKinds 通过 addAcceptKind 设置后会参与实际 Drop 判定,不匹配的卡牌不会产生 drop 事件。
  • newCard(defId) 返回的实例 id 形如 defId#N(保证唯一);卡牌定义用 getCardDefinition* 系列读取。

API 快查

  • eve.Card()registerCardsFromJsonclearCardDefinitionsgetCardDefinitionCounthasCardDefinitiongetCardDefinitionName/Kind/Cost/Attack/Health/TintR/TintG/TintB
  • eve.Card() 工厂:newConfig()newCard(defId)newDeck()newZone(id, label, x, y, w, h)newHand(cfg)
  • eve.Card() 状态:setConfig/getConfighandCount/getHand/findHandzoneCount/getZonegetDeckdrawCard(handOwner)
  • eve.Card() 每帧:update(dt, mx, my, down)render(gfx)renderDeck(gfx)
  • eve.Card() 事件:clearEventsgetEventCountgetEventTypegetEventHandgetEventZonegetEventCardIdgetEventReason
  • LayoutConfigget/setCardWget/setCardHget/setSpacingget/setHandXget/setHandYget/setArcHeightget/setRotationAngleget/setHoverRotationget/setHoverScaleget/setHoverLiftget/setHoverSpeedget/setMotionSpeedget/setDisabledAlphaget/setShowZonesget/setDeckXget/setDeckYget/setDragThreshold
  • CardDatagetId/setIdgetName/setNamegetKind/setKindgetCost/setCostgetAttack/setAttackgetHealth/setHealthisFaceUp/setFaceUpisDisabled/setDisabledgetState/setStategetTintR/G/BsetTint(r, g, b)setArt/getArtgetX/getY/getW/getH/getScale/getAlphaisHovered/isDragginghit(px, py)describe()
  • DeckpushdrawpeekcountisEmptyclearshufflegetCard
  • ZonegetId/setIdgetLabel/setLabelgetX/getY/getW/getHsetRectgetColorR/G/BsetColor(r, g, b)getAlpha/setAlphaisEnabled/setEnabledaddAcceptKindclearAcceptKindsacceptscontainsrender
  • HandgetOwner/setOwnergetConfig/setConfigisFaceDown/setFaceDownisPeek/setPeekisInteractive/setInteractiveaddCardremoveCardclearcountgetCardfindCardpickCardrender

使用要点

  • eve.Card()LayoutConfigHandDeckZoneCardData 实例都保存在全局或实体状态中,eve_init 里只建一次,不要每帧重建。
  • 一个 Hand 使用一份独立的 LayoutConfig(如敌方手牌新建一份并调 setHandY),不要共享后改字段。
  • hand.addCard 会把牌从 0 缩放平滑生长到槽位,形成“出牌”动画;拖拽未落入任何落牌区时自动归位。

源码: src/modules/card/
示例: examples/cardgame/
运行: make run/<platform>-debug GAME=examples/cardgame