Card Hand Toolkit — API Hub

#

How the toolkit fits together, a guide to each part, and a reference entry for every public type.


How to Use This Hub

#
  1. Read Concepts for the big picture.
  2. Read the Module for the part you're working on. Each one is a short guide: how its pieces fit, common tasks and code.
  3. Look types up in the A–Z Type Index. Each entry says what the type does, then lists its Inspector settings and key Members, and links to its module under Guide.

New to the toolkit? Start with the Quick Start.


Table of Contents

#

Concepts

#
Runtime hub supplies shared assets while game code updates hand state and hand views show the cards.

Who owns what?

#
Information Owner
A card design and its addon data ScriptableCard, listed in the CardDatabase.
One copy's identity CardRef: a slug plus a unique instance id. CardBehaviour carries that identity on its GameObject.
Draw and discard pile contents Your runtime Deck<CardRef>.
Hand membership, by seat HandStateService. Hand views follow its events.
Existing card visuals SceneCardLookup maps instance ids to CardBehaviours; it doesn't own game state.
Runtime display data and capabilities The card's ICardViewModel. Your code changes it and raises OnChanged.
Face-down, exhausted, hover and selection state CardBehaviour, which refreshes the card's views.
Binding and field visibility CardPresenter and HoverPresenter, using the ViewModel and Card Skin.
Rules, turns and effects Your game code.

Card lifecycle

#
A drawn CardRef enters hand state, reuses or builds its visual, then stays in hand or moves to an accepting play zone.

The hand checks SceneCardLookup before building a visual, so a card returning from a zone keeps its existing GameObject and ViewModel. In SingleSprite mode, the presenter skips the World View. The Card Data & Decks, Card Visuals and Zones & Play guides cover each step.

Extend, configure or learn

#

Every A–Z entry carries a tag:

Toolkit-owned assets: assets prefixed CHT_ (and everything in Samples/) are replaced when you update the package. Configure your own copies; the Setup Wizard creates them in Assets/CardHandToolkit/Config/.


Modules

#

Interaction

#

(hover, drag and tap, and what each card allows)

InteractionController chooses a zone or global policy, applies per-card overrides, and enables permitted interactions.

The components on a card

#

The card prefab (CHT_CardPlayable_Template, and your CardPlayable_User variant) has one component per interaction. Each reads the pointer from PointerService. Hover and tap ignore the pointer while it's over UI.

Permissions and policies

#

DefaultInteractionPolicy, used unless you replace it, allows:

Zone Hover Drag Select Target
Hand (the Player Hand) ✓ ✓ ✓
Board ✓ ✓ ✓
OpponentHand, Deck, Discard, others

There are three ways to change this, from broadest to narrowest:

  1. Globally: write a ScriptableObject that implements IInteractionPolicy, create an asset from it, and assign it to Override Policy Asset on CardToolkitRuntime (on your CHT_CardToolkitLoader).
  2. For one zone: implement IInteractionPolicyProvider on the zone, or on a component next to it. Its Policy replaces the global policy for cards in that zone.
  3. For one card: add a CardInteractionOverride and set its Allow and Deny masks. Deny wins over Allow.

A policy is asked again whenever a card changes zone. If yours also depends on game state, such as whose turn it is, call InteractionController.RefreshAll() when that state changes (or Refresh() on one card's controller):

using CardHandToolkit;
using UnityEngine;

[CreateAssetMenu(menuName = "My Game/Turn Interaction Policy")]
public class TurnInteractionPolicy : ScriptableObject, IInteractionPolicy
{
    public static bool IsMyTurn = true; // set by your turn system

    readonly DefaultInteractionPolicy defaults = new();

    public InteractionPerms GetPermissions(CardBehaviour card, InteractionContext ctx)
    {
        InteractionPerms perms = defaults.GetPermissions(card, ctx);
        if (!IsMyTurn)
            perms &= ~InteractionPerms.Drag; // look, but don't play
        return perms;
    }
}

// In your turn system:
TurnInteractionPolicy.IsMyTurn = false;
InteractionController.RefreshAll();

InteractionContext gives the card's Zone and its Owner: Local in the Player Hand, Opponent in an opponent hand, and Neutral everywhere else. To read what a card allows, for example before letting your targeting code pick it, use card.GetComponent<InteractionController>().Permissions.

Hover

#

The toolkit has two kinds of hover:

Most of the hover feel lives in the HoverProfile assigned on CardToolkitRuntime: the delays above, Hover Scale, Hover Anim Time, Hover Z Offset, the overlay offsets, and the highlight colors (Default Highlight Color for hovered or selectable cards, Selected Highlight Color for selected ones).

Out-of-hand previews are routed by HoverPolicyRuntime (on CHT_CardToolkitLoader) using a HoverRoutingPolicy asset (CHT_HoverRoutingPolicy by default):

The preview itself is drawn by a HoverPresenter in the scene, from the card's World View; see Card Visuals for its layout. If the scene has none, HoverPolicyRuntime creates one from its Presenter Prefab.

Pointer detection and touch arbitration update card hover state. In-hand hover changes the card pose; out-of-hand events pass through the routing policy to the overlay presenter.

HoverPointerArbiter claims a card per touch pointer; mouse claims are skipped by default. Overlay exclusivity is a separate decision made by HoverPolicyRuntime, which subscribes directly to CardBehaviour.OnAnyHoverEnter and OnAnyHoverExit.

For a custom hand's mouse-hover placement, implement IZoneHoverAnchorProvider. The Player Hand uses HoverPoseSolver to align the card's bottom edge with that anchor.

Tap and select

#

A click (press and release on the same card) or a short tap calls TapCard() on the card. A touch held longer than Touch Long Press Delay counts as a hover instead.

For example, to let the player pick a card from their hand:

foreach (CardRef cardRef in HandStateService.Instance.GetHand(0))
    if (SceneCardLookup.TryGet(cardRef.InstanceId, out CardBehaviour card))
        card.SetSelectable(true);

CardBehaviour.OnAnyCardSelected += picked => Debug.Log($"Picked {picked.Definition.cardName}");

Pointer input

#

PointerService polls one IPointerSource once per frame, so every component sees the same pointer. On desktop the mouse is reported every frame; on touch devices the first finger is, and touch takes priority over the mouse. The toolkit picks the source for your project's input setting: the Input System when the package is installed and active, otherwise the legacy Input Manager. To drive cards from something else, such as a gamepad-controlled cursor, implement IPointerSource and call PointerService.SetSource(yourSource). Both are in the CardHandToolkit.Internal namespace.

Zones & Play

#

(where cards live, and how a drag becomes a play)

Zones and resolvers

#

How a play works

#
A released card reorders, returns to hand, or enters the first accepting play resolver.
  1. The player drags a card out of the Player Hand. Above the hand's Play Height, the card highlights.
  2. When the card is released there, DragCard collects every active IPlayResolver in the loaded scenes, lowest Priority first. They're collected at each play, so zones added at runtime are included.
  3. It calls TryResolvePlay on each with a PlacementQuery: the card, the zone it came from, and where the pointer was released (screenPos, and pointerRay from the camera through it). The first resolver that returns allowed = true takes the card, and the rest aren't asked.
  4. If none accepts it, the card returns to its place in the hand.

Releasing the card over the hand itself (inside its reorder band) reorders the hand instead of playing.

The resolver that accepts a card moves it. The example zones do it in two steps:

  1. Remove the card from where it came from: HandStateService.Instance.RemoveCard(seat, card.ToCardRef()) for a hand (the hand then lets go of it), or fromZone.RemoveCard(card) for any other zone.
  2. Add it with AddCard(card), which parents it, applies the zone's Card Facing, and lays it out.

On the board, the default interaction policy lets a card be hovered and selected, but not dragged. See Interaction to change that.

The example zones

#

By default, the example zones ignore where the card is dropped. With Drop Area at (0, 0), they accept a play anywhere above Play Height, so when several are unlocked, the lowest Priority takes every play (equal priorities have no fixed order). To let the drop point choose the zone, give each one a Drop Area: a rectangle in the zone's local units, centered on the zone (on the Slot for SingleSlotZone), drawn as a yellow gizmo when the zone is selected. A play released outside it passes to the next resolver. Otherwise, lock the zones that shouldn't take plays right now, as the sample does between rounds.

Moving cards from code

#

Game code can move cards between hands and zones directly:

// Hand to zone: here, the opponent plays a card from its hand
HandStateService.Instance.RemoveCard(seat, cardRef);
if (SceneCardLookup.TryGet(cardRef.InstanceId, out CardBehaviour card))
    slot.AddCard(card);

// Zone to zone
slot.RemoveCard(card);
row.AddCard(card);

// Zone to hand: the hand finds the existing card and takes it back
row.RemoveCard(card);
HandStateService.Instance.AddCard(seat, card.ToCardRef());

Sample_GameManager uses the first two, and SingleSlotZone uses the third when it replaces a card.

Writing a resolver

#

Implement IPlayResolver on any component in the scene. This one destroys cards dropped on it:

using CardHandToolkit;
using UnityEngine;

public class BurnPile : MonoBehaviour, IPlayResolver
{
    [SerializeField] Vector2 size = new Vector2(3f, 4f);
    [SerializeField] int priority = 0; // asked before the example zones (1)

    public int Priority => priority;

    public PlacementResult TryResolvePlay(PlacementQuery query)
    {
        if (!query.IsOver(transform, size))
            return default; // not accepted: the next resolver is asked

        HandStateService.Instance.RemoveCard(query.fromZone.SeatId, query.card.ToCardRef());
        Destroy(query.card.gameObject);
        return new PlacementResult { allowed = true };
    }
}

Card Data & Decks

#

(what a card is, and how cards get from a deck into a hand)

Card definitions and unique CardRefs flow from a DeckList through a runtime deck and hand state to hand visuals.

Card data

#

One card, many copies: CardRef

#

A CardRef is one copy of a card: a CardSlug (which design) plus an InstanceId (which copy). Two Fireballs in a deck share a slug but have different instance ids, and CardRefs compare by instance id.

Decks

#

A DeckList asset lists the cards in a deck; list a card twice for two copies. deckList.ToSlugDeck(policy) creates a runtime Deck<CardRef> in list order, so shuffle it before drawing. The Deck is a plain C# object that your game code owns.

Member What it does
Draw(count = 1) Takes cards from the top. When the draw pile is empty, it asks the empty-deck policy first, and returns fewer cards if the deck runs out.
Peek(count = 1) The top cards, without drawing them.
InsertTop(card), InsertBottom(card) Puts a card back into the draw pile.
Discard(card) Adds a card to the discard pile.
Shuffle(), ShuffleRange(start, count) Shuffles the draw pile, or part of it (a negative start counts from the bottom).
Reshuffle() Moves the discard pile into the draw pile and shuffles.
Count, DiscardCount, SnapshotDrawPile(), SnapshotDiscardPile() Pile sizes and contents (draw pile: index 0 is top; discard pile: last entry is top).
CardDrawn, CardAdded, CardDiscarded, Shuffled, Reshuffled, CountsChanged, EmptyEvent Events for your UI and rules. CountsChanged passes the draw and discard counts; EmptyEvent fires when a draw finds the deck empty and the policy can't refill it.

The empty-deck policy decides what Draw does on an empty draw pile. ReshuffleOnEmpty (the default) shuffles the discard pile back in. NoReshufflePolicy doesn't, as in games where an empty deck ends the game. Implement IEmptyDeckPolicy<CardRef> for your own rule: return true once the draw pile has cards again.

Deck<CardRef> deck = deckList.ToSlugDeck(new ReshuffleOnEmpty<CardRef>());
deck.Shuffle();

foreach (CardRef card in deck.Draw(5))
    HandStateService.Instance.AddCard(0, card); // seat 0: the local player

Hands: HandStateService

#

HandStateService.Instance is the single record of which cards are in each seat's hand. Seat 0 is the local player (the Player Hand's Seat Id), and opponents use 1 and up. Hand views never add or remove cards on their own: change a hand through the service, and every hand view with that seat follows.

Showing the deck and discard pile

#

VisualDeck and VisualDiscard (prefabs CHT_DeckVisual and CHT_DiscardVisual) show a Deck's piles as stacks of cards. Bind them with Initialize(deck) after creating the deck.

Both share Max Visible Layers (default 8, minimum 1). The top layers use the normal fan layout; extra cards sit directly behind the bottom visible layer with separate depths to avoid flickering. Every card object is retained, and the last entry in each view's card list remains on top. The selected pile's gizmo previews the visible layers using the same layout.

Card Visuals

#

(how a card is built and drawn, and how to make it look like yours)

The Visual Factory initializes the mesh sprites and the World View data and visibility.

Two layers: the mesh and the World View

#

Each card is drawn in two layers, and they get their content from different places:

Quick Start Section 5 walks through restyling both layers, and Appendix A covers preparing art.

From card data to the screen

#

Three pieces sit between a ScriptableCard and the World View, so each can be replaced on its own:

To show your own data, such as a cost:

  1. Store it on the card with a CardAddon.
  2. Read it in your own view model, and call SetCapability("Cost", true) on cards that have it.
  3. Return that view model from your own ICardViewModelFactory, and assign it on the Visual Factory.
  4. Write the value into the World View in a binder that extends DefaultCardViewBinder.
  5. Add a FieldToggle with the custom key Cost to the element, and add Cost to the skin's Enabled Custom Fields.

Quick Start Section 6 builds this step by step, with Cost and Suit examples.

Changing cards at runtime

#

Finished card images: SingleSprite mode

#

If every card is one finished image, with no separate text or fields, set the Card Skin's Display Mode to SingleSprite, and put each card's finished image (5:7, opaque) in its Card Art. Cards then skip the World View; the opaque art covers the frame on the mesh; and the hover preview shows the art on its own.

Hands & Layout

#

(the player's and opponents' hands, and how cards fan out)

Hand gizmos mark Play Height, fan slots, the reorder band and Hover Anchor; focus raises and fans out the hand.

The Player Hand

#

PlayerHand shows the cards HandStateService holds for its Seat Id (0, the local player) and handles everything the player does with them: focusing, hovering, reordering and playing.

Opponent hands

#

Add one OpponentHand per opponent (prefab CHT_OpponentHand), with Seat Id 1, 2 and so on, and position and rotate it where that opponent sits. Its cards are face down, and the default interaction policy doesn't let the player hover or drag them. Change an opponent's hand through HandStateService with its seat id, and reveal a card with card.SetFaceDown(false). Seat Modifier tightens or loosens its fan (0.85 by default).

Fan Layout Profile

#

A FanLayoutProfile asset shapes the fan. The Player Hand, the opponent hands and the deck and discard piles each reference one; they use the shipped CHT_FanLayoutProfile until you assign your own (Create ▸ Card Toolkit ▸ Config ▸ Fan Layout Profile). Each curve is read from 0 (the leftmost card) to 1 (the rightmost):

Setting What it controls
X Curve Horizontal position across the hand.
Y Curve × Height Scale How high each card sits: the arc of the fan.
Z Curve × Z Amplitude, + Base Z Depth: which cards are drawn in front.
Rot Curve × Rot Degrees Scale How much each card tilts.
Max Hand Spread The hand stops widening past this many cards; more cards squeeze closer together.

Each hand or pile also has a FanSeatModifiers (Seat Modifier on hands, Spread Modifier on piles), whose Spread Mul scales the spread, arc and tilt: below 1 is tighter, and the piles use a small negative value to stack cards almost on top of each other. Select a hand or pile, or one of its parent objects, to preview its fan as a gizmo. Selecting a Fan Layout Profile asset also shows the previews for all player hands, opponent hands, deck piles and discard piles using it in the scene, with each hand or pile's own modifier and each pile's Max Visible Layers setting. Enable Gizmos in the Scene view; the previews update as you edit the profile, including Undo/Redo.

Piles calculate their spread from at most Max Visible Layers cards, so large decks keep distinct offsets between the visible layers. This limit applies only to VisualDeck and VisualDiscard; hand layouts still use every card.


A–Z Type Index

#

Legend: [E] Extend/Implement • [C] Configure (Inspector) • [L] Learn-only

Card3DView [C]

#

On the card mesh. Draws the frame, art and back through material property blocks, and the highlight glow (selected, highlighted or selectable cards). Set up in the card prefab; you rarely touch it.

CardAddon [E]

#

Base class for game-specific card data (cost, suit, attack…). Subclass it and add your own fields, then add one to a card with + Add New Addon in the ScriptableCard Inspector. It's saved inside the card asset.

CardBehaviour [L]

#

The root component of every card: its data, its instance id and its state flags (CardStateFlags). Game code talks to cards through it, and it passes state changes on to every ICardView under the card.

CardColorPalette [C]

#

A list of named colors (suits, rarities, factions…) for your binders to read by key. Assign one to a CardSkin's Color Palette; binders built on DefaultCardViewBinder read it through Palette. Create one with Create ▸ Card Toolkit ▸ Config ▸ Card Color Palette.

CardDatabase [C]

#

Every ScriptableCard your game can build. Assign it on CardToolkitRuntime, which indexes it by slug at startup. A card missing from it can't be built, and duplicate or empty slugs log a warning.

CardFrameLibrary [C]

#

The frames and card backs cards are drawn with, assigned on CardToolkitRuntime. A card's Frame Key picks a frame; an empty key uses the Default Frame, and a key with no match warns once and uses it too.

CardHoverable [C]

#

On every card. Detects hover: pointer-over in the hand, and a short delay (mouse) or long press (touch) elsewhere. Its timings come from the HoverProfile. It's switched off when the card's permissions don't include Hover.

CardPoseView [L]

#

On the card's view root. Animates the card mesh from its state: flips face-down cards, tilts exhausted (tapped) ones, and scales and brings forward cards hovered in the hand, using the Hover Profile's Hover Scale, Hover Anim Time and Hover Z Offset.

CardPresenter (sealed) [L]

#

On each card: instantiates the Card Skin's World View Prefab, binds the card's view model to it, and shows or hides fields. Every World View is laid out at ViewDesignSize (1000×1400) and scaled to fit the card, so a layout looks the same on cards, in the hover overlay and in Prefab Mode. In SingleSprite mode it builds no World View.

CardRef [L]

#

One copy of a card: a CardSlug (which ScriptableCard it is) plus a unique InstanceId (which copy). Two CardRefs are equal when their instance ids match. DeckList.ToSlugDeck() creates them for you.

CardSkin [C]

#

Global look settings for every card, assigned on the Visual Factory: which World View cards use, how big it appears on cards and in the hover overlay, which fields show, and colors.

CardTap [C]

#

On every card. Turns a click, or a tap shorter than the Hover Profile's Touch Long Press Delay, into CardBehaviour.TapCard(), which raises the tap events and toggles selection on selectable cards. It's switched off when the card's permissions don't include Select.

CardToolkitRuntime [C]

#

The toolkit's runtime hub, on the CHT_CardToolkitLoader prefab, which every scene with cards needs (the Setup Wizard adds and wires it). It holds the project-wide references every system reads and persists across scenes; when scenes change, the first instance stays and later ones remove themselves. It logs an error on Play if no instance exists, and adds an EventSystem with the right input module to any scene without one. Cards need no dedicated layer: hover, drag and tap raycast against Card Raycast Mask and ignore trigger colliders.

CardVisualFactorySO [C]

#

The built-in ICardVisualFactory (the Setup Wizard creates CardVisualFactory_User). Builds a card: looks up its data by slug, instantiates your card prefab, initializes its CardBehaviour, creates its view model and initializes its CardPresenter.

CardZoneTracker [C]

#

On every card. Tracks the ICardZone the card is in (the nearest one among its parents) and its home zone, the hand it belongs to. SingleSlotZone sends a replaced card back to its home zone's seat.

Deck [L]

#

Deck<TId> is a runtime draw pile plus discard pile, a plain C# object your game code owns. The toolkit uses Deck<CardRef>, created from a DeckList. Index 0 is the top of the draw pile; the last entry is the top of the discard pile.

DeckList [C]

#

The cards in a deck, as an asset (Create ▸ Card Toolkit ▸ Deck List). List a card twice for two copies.

DefaultInteractionPolicy [L]

#

The interaction policy used when no Override Policy Asset is assigned on CardToolkitRuntime. In the hand: hover, drag and select. On the board: hover, select and target. Everywhere else: nothing. Your own policy can call it for its defaults and change only what it needs.

DefaultCardViewBinder [E]

#

The built-in ICardViewBinder, on the World View. Fills the name, rules text, art and frame from any view model (art and frame are used by the hover preview; cards in the scene draw those on the mesh). Every slot is optional. Subclass it to fill your own elements.

DefaultViewModelFactory / DefaultCardVM [L]

#

The built-in view model factory (asset CHT_ViewModelFactory) and the view model it creates. DefaultCardVM fills the built-in fields from the ScriptableCard and has no custom fields. Replace both when your cards show custom data.

DragCard [C]

#

On every card. Drags a hovered card once the pointer moves past a threshold, and on release either reorders the hand, plays the card (above Play Height), or lets it return. It's switched off when the card's permissions don't include Drag.

FanLayout / FanPose [L]

#

The math behind the fan: FanLayout.ComputeFromProfile turns a card count, a hand width, a FanLayoutProfile and FanSeatModifiers into one FanPose (local position and rotation) per card. Hands and piles call it; use it yourself for a custom hand view. In CardHandToolkit.Internal.

FanLayoutProfile [C]

#

The shape of a fan of cards, shared by hands and piles (CHT_FanLayoutProfile by default; Create ▸ Card Toolkit ▸ Config ▸ Fan Layout Profile for your own).

FieldToggle [C]

#

On a World View element. Shows or hides it from the Card Skin's enabled fields and the view model's data and capabilities. CardPresenter and HoverPresenter apply every toggle in the view.

HandStateService [L]

#

The single record of which cards are in each seat's hand (seat 0 is the local player). Hand views follow it and never change hands on their own.

HandViewBase [L]

#

Base class of PlayerHand and OpponentHand: a seat id, and the two handlers a hand view implements to follow HandStateService. Extend it (and implement ICardZone) for a hand view of your own.

HoverAnchor [C]

#

A child of the Player Hand whose gizmo (a cyan line) marks where the bottom of a mouse-hovered card lifts to. Keep it at or below the bottom edge of the screen. Its position is set from a reference card's size.

HoverAnchorInfo / AnchorSpace [L]

#

Anchor data returned by IZoneHoverAnchorProvider for mouse-hover placement. Both types are in CardHandToolkit.

HoverPolicyRuntime [C]

#

On CHT_CardToolkitLoader. Routes out-of-hand hovers to the HoverPresenter as previews, following its HoverRoutingPolicy, and hides previews when a drag ends or the scene changes.

HoverPoseSolver [L]

#

Internal mouse-hover placement helper in CardHandToolkit.Internal. The Player Hand uses the zone's anchor and the card's footprint to find a local Y where the card's bottom rests on the anchor line.

HoverPresenter / IHoverPresenter [C]/[E]

#

HoverPresenter is the overlay (prefab CHT_HoverPresenter, with its own Canvas) that shows the large preview of an out-of-hand hovered card. With a mouse it appears next to the card; with touch it appears at the left edge over a backdrop that closes it when tapped. Implement IHoverPresenter to show previews your own way.

HoverProfile [C]

#

Hover timings and feel, assigned on CardToolkitRuntime (the Setup Wizard creates one).

HoverRoutingPolicy [C]

#

Rules for out-of-hand previews, assigned on HoverPolicyRuntime (CHT_HoverRoutingPolicy by default; Create ▸ Card Toolkit ▸ Config ▸ Hover Routing Policy for your own).

ICardView [E]

#

Anything under a card that reacts to its sprites and state. CardBehaviour finds every ICardView under the card when it's created, sends them the frame, art and back, and calls Refresh(state) whenever a state flag changes. Card3DView and CardPoseView implement it; implement it yourself to react to state, for example to dim disabled cards.

ICardViewBinder [E]

#

Writes a view model's data into a World View's UI. It only sets data; field toggles decide what shows. Start from DefaultCardViewBinder rather than implementing this directly.

ICardViewModel [E]

#

The data a card displays, created per card by the view model factory. Implement it for custom data or runtime state (a cost that changes, counters, buffs).

ICardViewModelFactory [E]

#

Creates the ICardViewModel for each card. Implement it on a ScriptableObject, create an asset, and assign it to your Visual Factory's View Model Factory SO.

ICardVisualFactory [E]

#

Builds a card from a CardRef. CardVisualFactorySO is the built-in one; to replace it, implement this on a ScriptableObject and assign it to Visual Factory on CardToolkitRuntime. Hands and piles call it for cards that aren't in the scene yet.

ICardZone [E]

#

Anything that holds and lays out cards: hands, board zones, and deck and discard piles. A card is in a zone when the zone's GameObject is one of its parents.

IContextualSwitcher [E]

#

Lets a component in the World View look different on cards in the scene and in the hover overlay, which reuse the same prefab. Both use the same 1000×1400 layout, so you only need this for deliberate differences (font sizes, spacing, visibility).

An icon that only appears in the overlay:

public sealed class OverlayOnlyIcon : MonoBehaviour, IContextualSwitcher
{
    public void ApplyWorld() => gameObject.SetActive(false);
    public void ApplyOverlay() => gameObject.SetActive(true);
}

IEmptyDeckPolicy / ReshuffleOnEmpty / NoReshufflePolicy [E]/[C]

#

Decides what a Deck does when you draw from an empty draw pile. ReshuffleOnEmpty (the default) shuffles the discard pile back in; NoReshufflePolicy doesn't. Implement IEmptyDeckPolicy for your own rule.

IInteractionOverride / CardInteractionOverride [C]/[E]

#

Changes one card's permissions after the policy has decided. Add a CardInteractionOverride and set its masks, or implement IInteractionOverride on your own component. Deny wins over Allow. Call Refresh() on the card's InteractionController after changing the masks.

IInteractionPolicy [E]

#

Decides what the player may do with a card (InteractionPerms: Hover, Drag, Select, Target) given its InteractionContext (Zone and Owner). Replace the global policy with a ScriptableObject that implements it, assigned to Override Policy Asset on CardToolkitRuntime. A zone can supply its own by implementing IInteractionPolicyProvider (Policy) on the zone or a component next to it.

InteractionController [L]

#

On every card. Asks the zone's policy (or the global one) what the card allows, applies any IInteractionOverride, then switches CardHoverable, DragCard and CardTap on or off. It asks again whenever the card changes zone.

IPlayHeightZone / IReorderBandZone / ILockableZone [E]

#

Optional zone capabilities, used by DragCard on the zone a card is dragged from.

IPlayResolver [E]

#

Decides whether to accept a card the player plays. Every active resolver is asked in Priority order (lowest first) until one accepts. The one that accepts moves the card.

IZoneHoverAnchorProvider [E]

#

Supplies the line a mouse-hovered card's bottom edge rests on. Implement it alongside a custom hand or zone that uses anchored hover placement; PlayerHand already does. Providing the anchor alone doesn't animate cards: your layout code calls the solver and applies the result.

LineBoardZone (example) [C]

#

A board zone that lays cards out in a centered row. Use it as is or as a starting point for your own.

OpponentHand [C]

#

An opponent's hand (prefab CHT_OpponentHand): shows the cards HandStateService holds for its seat, face down, in a fan. Add one per opponent.

PlayerHand [C]

#

The local player's hand (prefab CHT_PlayerHand; the Setup Wizard always adds one). Shows the cards HandStateService holds for its seat as a fan, and handles focusing, hovering, reordering and playing.

PointerService / IPointerSource [L]/[E]

#

The single pointer every card component reads. PointerService polls one IPointerSource per frame: the Input System source when that package is installed and active, otherwise the legacy Input Manager source. Touch takes priority over the mouse. Both types are in CardHandToolkit.Internal.

SceneCardLookup [L]

#

Finds the card in the scene for a CardRef. Every card registers itself when it's built and unregisters when destroyed, so hands and piles reuse existing cards instead of building duplicates.

ScriptableCard [C]

#

One card design (Create ▸ Card Toolkit ▸ Card). Copies in play are CardRefs that point to it by slug.

SingleSlotZone (example) [C]

#

A board zone that holds one card. Use it as is or as a starting point for your own.

VisualDeck / VisualDiscard [C]

#

Show a Deck's draw pile (face down) and discard pile (face up) as stacks of cards. They're views: they follow the deck's events and ignore AddCard/RemoveCard. Prefabs: CHT_DeckVisual, CHT_DiscardVisual.


FAQ

#

Common questions and troubleshooting are in the FAQ.


Next: New to the toolkit? Start with the Quick Start. Otherwise jump to a module: Interaction • Zones & Play • Card Data & Decks • Card Visuals • Hands & Layout

Back to top