Template of a ZScript animated monster made with Decoupled IQM animations

Handy guides on how to do things, written by users for users.

Moderators: GZDoom Developers, Raze Developers

Forum rules
Please don't start threads here asking for help. This forum is not for requesting guides, only for posting them. If you need help, the Editing forum is for you.
solvictusig
Posts: 5
Joined: Sun Oct 22, 2023 8:13 am
Preferred Pronouns: He/Him
Operating System Version (Optional): etc
Graphics Processor: Not Listed

Template of a ZScript animated monster made with Decoupled IQM animations

Post by solvictusig »

Based on this guide, I created a monster with proper movement logic and animation:
viewtopic.php?t=79003

Code: Select all

class testmonster: actor
{
    default    
    {
        Health 500;
        Radius 20;
        Height 64;
        Mass 600;
        Speed 2;
        PainChance 200;
        Monster;
        +FLOORCLIP
        +DECOUPLEDANIMATIONS
    }
   double targetAngle;

const rotSpeedMin = 5.1;

const rotMult = 0.3;

override void PostBeginPlay()
{
    targetAngle = angle;
}

override void Tick()
{
    Super.Tick();
    
    if(angle != targetAngle)
    {
        double diff = DeltaAngle(angle, targetAngle);
        
        //cap rotation speed to min speed
        double rot = max(abs(diff) * rotMult , rotSpeedMin);
        
        if(rot > abs(diff))
        { // don't overshoot the angle when rotating
            angle = targetAngle;
        }
        else
        {
            if(diff < 0)
            {
                A_SetAngle(angle - rot, SPF_INTERPOLATE);
            }
            else
            {
                A_SetAngle(angle + rot, SPF_INTERPOLATE);
            }
        }
    }
}
 
    States
    {
Spawn:
	M000 A 0 {
    double oldAngle = invoker.angle;
    A_Look();
    invoker.targetAngle = invoker.angle;
    invoker.angle = oldAngle;
}
	M000 A 1 NoDelay SetAnimation('idle', flags:SAF_LOOP|SAF_NOOVERRIDE);
	loop;
See:
	M000 A 0 {
    double oldAngle = invoker.angle;
    A_Chase();
    invoker.targetAngle = invoker.angle;
    invoker.angle = oldAngle;
}
	M000 A 1 NoDelay SetAnimation('walking', flags:SAF_LOOP|SAF_NOOVERRIDE);
	loop;
Melee:
	M000 A 1 {
    double oldAngle = invoker.angle;
	A_CustomMeleeAttack (1);
    invoker.targetAngle = invoker.angle;
    invoker.angle = oldAngle;
}
	M000 A 34 NoDelay SetAnimation('attack', flags:SAF_LOOP|SAF_NOOVERRIDE);
    goto See;
Pain:
	M000 A 1 {
    double oldAngle = invoker.angle;
    A_Pain();
    invoker.targetAngle = invoker.angle;
    invoker.angle = oldAngle;
}
	M000 A 42 NoDelay SetAnimation('pain', flags:SAF_LOOP|SAF_NOOVERRIDE);
    goto See;
 Death:
M000 A -1 SetAnimation('dying');
	stop;
    }
}
The first part is the logic of the move.
In Melee 34, it's the last frame of the attack animation.
In Pain 42, it's the last frame of the dying animation.

Regarding porting from Blender:

I used this plugin: https://github.com/lsalzman/iqm
It only worked for me with Blender version 2.93.18.
Details about the porting are in the image:
Image

p.s. Thanks Jay from the official discord vkdoom and of course Jekyll Grim Payne

Return to “Tutorials”