Files
test/agent-skills/05-skill-gfx-deep-dive.md
2026-02-24 09:50:08 +07:00

9.1 KiB

Skill GFX System Deep Dive

Movement System Architecture

CGfxMoveBase Design

Purpose: Abstract base for all projectile movement patterns.

Key Responsibilities:

  • Calculate position over time
  • Handle area emission (random offsets)
  • Support clustering (multiple projectiles)
  • Provide position/direction queries

Core Methods:

public abstract void StartMove(Vector3 vHost, Vector3 vTarget);
public abstract bool TickMove(uint dwDeltaTime, Vector3 vHostPos, Vector3 vTargetPos);

Return Value Semantics:

  • StartMove(): Initialize movement, set initial position/direction
  • TickMove():
    • Returns true = target reached/hit
    • Returns false = still in flight
    • Updates m_vPos and m_vMoveDir

Movement Mode Details

CGfxLinearMove

  • Pattern: Straight line from host to target
  • Speed: _fly_speed = 20.0f / 1000.0f (units per millisecond)
  • Behavior:
    • If max fly time allows, uses constant speed
    • Otherwise, calculates speed to reach target in max time
    • Updates direction each frame to track moving target

CGfxOnTargetMove

  • Pattern: Instant hit (no flight)
  • Behavior:
    • StartMove(): Sets position to target immediately
    • TickMove(): Returns false (stays at target)
    • Hit triggered by fly time timeout, NOT by TickMove() return
    • Supports cluster offset (random radius around target)

CGfxParabolicMove

  • Pattern: Arc trajectory with gravity
  • Physics:
    • Horizontal velocity constant
    • Vertical velocity affected by gravity
    • Calculates initial vertical velocity to reach target

CGfxMissileMove

  • Pattern: Homing missile
  • Behavior:
    • Accelerates toward target
    • Rotates to face target
    • Constant acceleration until hit

CGfxMeteoricMove

  • Pattern: Falls from sky
  • Behavior:
    • Starts above target
    • Falls straight down
    • Configurable radius

CGfxHelixMove

  • Pattern: Spiral path
  • Behavior:
    • Spiral around center axis
    • Radius can shrink over time
    • Configurable radius parameter

CGfxCurvedMove

  • Pattern: Bezier-like curve
  • Behavior:
    • Curves from host to target
    • Can curve left or right
    • Configurable lateral speed

CGfxAccMove

  • Pattern: Accelerated flight
  • Behavior:
    • Constant acceleration
    • Configurable acceleration value

CGfxLinkMove

  • Pattern: Lightning chain
  • Behavior:
    • Links between host and target
    • Updates GFX parameters dynamically
    • No movement (stays linked)

CGfxRandMove

  • Pattern: Random walk
  • Behavior:
    • Random direction changes
    • Configurable step size and speed
    • Uses control points for smoothness

Area Emission System

Purpose

Emit multiple projectiles in a random area around the start position.

Configuration

  • m_bArea: Enable area emission
  • m_Shape: Emission shape (Box, Sphere, Cylinder)
  • m_vSize: Size of emission area

Implementation

CalcRange(): Calculates X/Y/Z range vectors based on movement direction.

protected void CalcRange(Vector3 vDir)
{
    m_vYRange = Vector3.up;
    m_vZRange = new Vector3(vDir.x, 0, vDir.z);  // Project to horizontal
    if (Normalize(ref m_vZRange) < 0.01f) m_vZRange = Vector3.forward;
    m_vXRange = Vector3.Cross(m_vYRange, m_vZRange);  // Perpendicular
    // Scale by m_vSize
}

GetRandOff(): Generates random offset based on shape.

  • Box: Uniform distribution in box
  • Sphere: Uniform distribution in sphere (rejection sampling)
  • Cylinder: Uniform in horizontal circle, uniform in Y

Usage

if (m_bArea)
{
    CalcRange((vTarget - vHost).normalized);
    m_vPos = vHost + GetRandOff();
}

Clustering System

Purpose

Emit multiple projectiles with time intervals (e.g., machine gun effect).

Configuration

  • m_bOneOfCluser: This projectile is part of a cluster
  • Cluster params: Count and interval (handled by manager)

OnTarget Clustering

For CGfxOnTargetMove, clustering adds random offset:

if (m_bOneOfCluser)
{
    float fRandAng = Random.value * 2π;
    float fRadius = Random.value * m_fRadius;
    m_vOffset = new Vector3(cos(ang) * radius, 0, sin(ang) * radius);
    m_vPos += m_vOffset;
}

State Machine Details

enumWait State

  • Entry: Event created
  • Behavior:
    • Increment m_dwCurSpan
    • Wait for m_dwDelayTime
  • Exit: When m_dwCurSpan >= m_dwDelayTime
  • Transition: → enumFlying

enumFlying State

  • Entry: Delay expired
  • Behavior:
    • Call m_pMoveMethod.SetMaxFlyTime(m_dwFlyTimeSpan)
    • Call m_pMoveMethod.StartMove(m_vHostPos, m_vTargetPos)
    • Each frame:
      • Update host/target positions if m_bTraceTarget
      • Call m_pMoveMethod.TickMove(dwDeltaTime, ...)
      • Update GFX position
      • Check timeout: m_dwCurSpan > m_dwFlyTimeSpan
  • Exit Conditions:
    • TickMove() returns true (target hit)
    • m_dwCurSpan > m_dwFlyTimeSpan (timeout)
  • Transition: → enumHit

enumHit State

  • Entry: Target hit or timeout
  • Behavior:
    • Release fly GFX (fade out or immediate)
    • Spawn hit GFX at target position
    • Update hit GFX position if m_bTraceTarget
    • Check if hit GFX is infinite (m_bHitGfxInfinite)
  • Exit Conditions:
    • Hit GFX completes (not infinite)
    • Hit GFX timeout (5 seconds for infinite)
    • Target disappears
  • Transition: → enumFinished

enumFinished State

  • Entry: Hit effect complete
  • Behavior:
    • Release all GFX
    • Mark event for cleanup/pooling
  • Exit: Event removed from active list

Position Tracking

Host Position

  • Source: Character position from m_nHostID
  • Update: Each frame if m_bHostExist
  • Usage:
    • Initial position for StartMove()
    • Dynamic updates for TickMove() (if target moves)

Target Position

  • Source: Character position from m_nTargetID
  • Update: Each frame if m_bTargetExist and m_bTraceTarget
  • Usage:
    • Target position for StartMove()
    • Dynamic updates for TickMove() (tracking)

GetTargetCenter()

  • Abstract method: Must be implemented by derived class
  • Purpose: Get current target center position
  • Implementation:
    • CECSkillGfxEvent: Gets character position from ID
    • Can use hooks/bones in future

GFX Loading & Instantiation

C++ Pattern

A3DGFXEx* LoadFlyGfx(A3DDevice* pDev, const char* szPath);
void SetFlyGfx(A3DGFXEx* pFlyGfx);

C# Pattern (Unity)

// Async loading with Addressables
async Task<GameObject> LoadFlyGfxAsync(string path)
{
    var handle = Addressables.LoadAssetAsync<GameObject>(path);
    await handle.Task;
    return handle.Result;
}

// Instantiation
GameObject instance = Instantiate(prefab, position, rotation);

Lifecycle

  1. Load: Async load prefab (Addressables)
  2. Instantiate: Create instance at position
  3. Update: Update position each frame
  4. Release: Destroy instance, release prefab

Parameter System

GFX_SKILL_PARAM Structure

public struct GFX_SKILL_PARAM
{
    public ValueUnion value;  // Union: bVal, nVal, or fVal
    public bool m_bArea;
    public EmitShape m_Shape;
    public A3DVECTOR3 m_vSize;
}

Usage by Movement Mode

  • CGfxLinearMove: Uses m_bArea, m_Shape, m_vSize (default)
  • CGfxOnTargetMove: Also uses value.fVal for radius
  • CGfxMeteoricMove: Uses value.fVal for fall radius
  • CGfxHelixMove: Uses value.fVal for spiral radius
  • CGfxCurvedMove: Uses value.bVal for curve direction
  • CGfxAccMove: Uses value.fVal for acceleration
  • CGfxRandMove: Uses value.fVal for speed

Setting Parameters

GFX_SKILL_PARAM param = new GFX_SKILL_PARAM();
param.value.fVal = 5.0f;  // Radius for OnTarget
param.m_bArea = true;
param.m_Shape = EmitShape.enumSphere;
param.m_vSize = new A3DVECTOR3(2, 2, 2);

m_pMoveMethod.SetParam(param);

Performance Optimizations

Object Pooling

  • Purpose: Reuse event instances
  • Implementation: m_FreeLst in CECSkillGfxMan
  • Usage:
    • Get from pool when creating event
    • Return to pool when finished

GFX Caching

  • Purpose: Avoid reloading same prefabs
  • Implementation: Cache loaded Addressable handles
  • Key: Use path as key

Batch Updates

  • Purpose: Update all events efficiently
  • Implementation: Single loop through active events
  • Optimization: Early exit for finished events

LOD System

  • Purpose: Reduce quality for distant effects
  • Field: m_bGfxUseLod
  • Usage: Disable expensive effects when far away

Debugging Tips

Common Issues

  1. Projectile doesn't move: Check StartMove() called, m_fSpeed set
  2. Projectile goes wrong direction: Check m_vMoveDir calculation
  3. Hit doesn't trigger: Check TickMove() return value, timeout
  4. GFX doesn't appear: Check loading, instantiation, position
  5. Performance issues: Check pooling, caching, LOD

Debug Fields

  • m_vPos: Current position
  • m_vMoveDir: Current direction
  • m_enumState: Current state
  • m_dwCurSpan: Current time span
  • m_dwFlyTimeSpan: Max fly time

Visualization

  • Draw gizmos for projectile position
  • Draw line from host to target
  • Show state in inspector
  • Log state transitions