Jump to content

Seravy's A.I. Guide


Ryon

Recommended Posts

Seravy's A.I. guide from the Mugen Wiki

Contents

General Information

This guide is about coding custom AI for mugen. Custom AI means the character acts the way you program it to, and ignores the default "AI" of the engine completely. This is a very important part in characters because this is the way to make characters more intelligent, making the fight more challenging and interesting. The default "AI" doesn't know what each move is for, and just executes them randomly, more often at higher difficulty and less often on low. This however, makes the character look very dumb, because they don't know such basic things as the range and area of their attacks, so pretty often, they are punching the air, or even worse, use suicide attacks when they aren't supposed to. Creating a custom AI can prevent this kind of behavior, by telling exact rules about when and how to use the attacks. An AI consists of three parts: Activation, Commands, and common/system behavior.

AI activation

AI activation is the part where we decide when to turn the AI on or off.

AI activation for winmugen/beta mugen

Pre 1.0 mugen versions don't support custom AI directly, so activating an AI is only possible using workarounds. There were several workarounds for this, each having their own strengths and weaknesses. A quick list of the possible methods were

  • Human Impossible commands
  • Helper method
  • XOR method
  • Ishometeam method

Winane's AI activation code combined all four together for optimal performance, however, it is pretty complicated and hard to understand. Improper, simplified implementation of the XOR or Helper methods can have false positives, so these methods are not recommended for beginners.

Detecting the difficulty level in older mugen is impossible, so the AI will always fight on a pre-set level instead of what the player selected. Most character make it possible to select this level by directly editing a configuration file of the character, but considering how many characters you usually have in your mugen, changing difficulty level this way, one-by-one for every character is completely unreasonable. This is the main reason why winmugen is outdated, and not recommended: Being able to play on the desired difficulty level is a very basic feature, and the lack of it hurts gaming experience a lot.

AI activation for mugen 1.0

Mugen 1.0 and above has a new trigger: AILevel that supports custom made AI properly. By using this trigger in your AI code parts directly, you can create an AI without an activation part or variables. However, doing so is not recommended, as it reduces flexibility a lot, and makes it much harder to scale your difficulty levels, or turn the AI on/off on special conditions when necessary.

Generally, you'll need two variables for your AI activation. The first variable is the AI variable itself. If it is set to 0, your AI is off, and if it is 1, it is on. Other values can be used if you want multiple AI modes, like a special boss mode for example. The second variable is the difficulty variable. This variable is responsible in determining the chance for attacking: higher values would attack more often, while lower values attack less often, making the AI's reaction time slower, and closer to a human player's. The two can be stored in one variable (0= AI off, 1-1000 =AI on with difficulty chance stored) if you want to save variable space.

Example code below, it needs to be placed into one of your minus states:

var(59) = AI variable
var(50) = Difficulty variable

[State -1, AI ON] ; Turn the AI on when
Type = VarSet
TriggerAll = Var(59) < 1; it is not on yet and
TriggerAll = RoundState=2 ; the fight has started and is not over yet and
Trigger1 = AILevel>0 ; the computer is playing the character
v = 59
value= 1 ; value of 1 will mean the AI is on
Ignorehitpause=1

[State -1, AI OFF] ; Turn the AI off when
Type=VarSet
Trigger1=var(59)>0 ; it is on and
Trigger1=RoundState!=2 ; the round is not started yet or is already over
Trigger2=!IsHelper ; OR if we are a player, but
Trigger2=AILevel=0 ; the computer is not in control
v=59
value=0 ; value of 0 will mean the AI is off. values other than 0 and 1 are not used in this example, we have only one AI mode, the normal one.
Ignorehitpause=1

[State -1]
Type=VarSet
Trigger1=1
var(50)=(AILevel=1)*3+(AILevel=2)*7+(AILevel=3)*16+(AILevel=4)*30+(AILevel=5)*58+(AILevel=6)*90+ (AILevel=7)*150+(AILevel=8)*300

Numbers here correspond to the chance for attacking from 1 to 1000. Higher number means less reaction time. Lower number means more. Linear distribution of difficulty (using AILevel*10 for example) is not recommended. Numbers below 10-15 will make the character attack rarely, making the AI play weaker than a human. These values are recommended for the first two (easy) difficulty levels. Numbers around 20-50 are causing the AI to fight more like a human player, attacks will usually have a bit of a reaction time, but generally, the AI will not stay idle for too long and will fight actively. Numbers over 50-100 will reduce the reaction time of the AI significantly, and 1000 completely eliminates it. 1000 will also make the AI perform the first matching attack in each situation unless coded otherwise. These values are recommended for hard (6-8) difficulty levels only, as the reaction time is way faster than what a human player has. Using numbers above 300 is not recommended, as it doesn't make the AI any harder, but it does make it more determined/easier to predict.

Average reaction time in ticks for various var(50) values, assuming a situation when only one attack is matching the triggers and is available to use (the current tick not included):

  • 1000 - 0, always attack immediately with the first matching attack
  • 900 - 0.11 tick
  • 800 - 0.25 tick
  • 700 - 0.42 tick
  • 600 - 0.66 tick
  • 500 - 1 tick
  • 400 - 1.5 ticks
  • 300 - 2.33 ticks
  • 200 - 4 ticks
  • 100 - 9 ticks

If we consider the human reaction time to be 1/6th of a second which is 10 ticks, then it is clearly visible that any values over 100 are way too fast against a human player for normal levels. Especially as in normal cases, multiple attacks are available for the AI, further reducing the reaction time: 2 available attacks mean half reaction time on average, three means a third, and 10 means a tenth of it only, so the zone over 100 is really meant for hard to impossible difficulty levels. As visible from this, there is basically no difference between values over 500 at all, so linear scaling fails the most in that area: the attacks get 0-1 tick of delay most of the time.

Below 100, the reaction time increases non-linearly:

  • 90 - 10.1 ticks
  • 80 - 11.5 ticks
  • 70 - 13.2 ticks
  • 60 - 15.6 ticks
  • 50 - 19 ticks
  • 40 - 24 ticks
  • 30 - 32 ticks
  • 20 - 49 ticks
  • 15 - 65 ticks
  • 10 - 99 ticks
  • 9 - 110 ticks
  • 8 - 124 ticks
  • 7 - 141 ticks
  • 6 - 166 ticks
  • 5 - 199 ticks
  • 4 - 249 ticks
  • 3 - 332 ticks
  • 2 - 499 ticks
  • 1 - 999 ticks

making it an ideal range for easy and normal difficulty levels: values below 10 have a reaction time of an entire second to many seconds, while values over 15 have 1 second or less, making it ideal for normal difficulty. Note that characters having more attacks available per situation need slightly lower var(50) values than characters with fewer ones, so in most cases, only the half/third/fourth of this reaction time applies in reality for those situations.

Overall, for easy levels, take numbers from the 1-15 interval, for normal, take ones from 10-50, and for hard, take from 50-200 as a general advice. Characters with more moves or better/more versatile moves will generally need lower values like 1-10 for easy, 10-30 for normal and 30-100 for hard.

Do note that "reaction time" here means time spent doing nothing before deciding what attack to use, not time spent idle after deciding to use an attack. Also note that all of these are average values: since random is used, there is always a chance for any amount of reaction time from zero to infinite, but during a whole match duration, these even themselves out. To implement an AI that has a real, human like reaction time (delay between deciding on an attack and using it), a more complicated and advanced system is necessary, where you use a timer variable and buffer the attacks that need to be executed, although I don't see much of a reason to go that far: if you want your AI to mess up and use attacks at the wrong timing, you can do that in an easier way by adding low level specific triggers to your attacks, instead of simulating a reaction time.

Coding AI moves

This is the part where you tell the AI when and how they are supposed to use the moves available. It works pretty much the same way as for the player, except for the conditions themselves. They are changestates that need to activate when the AI wants to use the move, and are placed in state -1. They can be placed in state -3 as well, but placing them into -1 makes more sense. These are pretty much the AI equivalent of a human player's command input.

First of all, while the AI is on, we must disable all human player commands from working. The engine spams player commands (this is how the built in AI of the engine works: it simulates commands coming from the keyboard), and we don't want these to have any effect on our character. To do this, add

Triggerall=var(59)>0
to all of your regular command changestates.

Then, to make the AI able to use the commands, you must make a copy of that changestate, remove the conditions limited to human players (this almost always only means the command="xxx" lines), and add new ones for the AI. An example:

Type=Changestate
Triggerall=command="super"
Triggerall=power>=3000
Triggerall=Statetype=S
Triggerall=life<=0.5*Lifemax
Trigger1=ctrl
Trigger2= Stateno=1000
Trigger2= Movehit
value=4000

This is a command block for a human player, it describes a super move that can be used when at 3000 or more power, and below half of maximum life while standing and having control, or as a combo after an attack coded in state 1000. The AI equivalent would be

Type=Changestate
Triggerall=var(59)>0 ; Use this only if the AI is ON
Triggerall=power>=3000 ; Move cost still applies to the AI
Triggerall=Statetype=S ; Game rules still apply to the AI
Triggerall=life<=0.5*Lifemax ; Game rules still apply to the AI. Removing this line would make the AI cheat by using the move with over half life left.
Triggerall=abs(P2Bodydist X)<40 ; use this move only when the opponent is close enough. Moves usually don't hit the entire screen, so specifying the distance this way is necessary
Triggerall=P2StateType=S; use it only if the opponent is standing. This way, we don't need to worry about the y position of the opponent, and we won't use it when the opponent is on the ground or is falling. Using other triggers in other cases like P2dist Y, enemynear,pos Y, P2Stateype!=L,!enemynear,hitfall, etc... might be necessary.
Triggerall=!Inguarddist; Don't use it if in guard distance. We don't want to run into a hit and get our super interrupted. This line is unnecessary if the super cannot be interrupted by hits.
Triggerall=random<var(50)*1.1; 1-1.5="" a="" ai="" and="" around="" attack="" attack.="" attacks="" attacks.="" be="" br="" by="" can="" chance="" combo="" controlled="" determines="" difficulty="" dodging="" easy="" for="" from="" higher="" how="" in="" is="" it="" level.="" likely="" line="" long="" lower="" most="" multiplier="" of="" on="" only="" or="" powerful="" recommended="" reduces="" s="" so="" starter="" t="" that="" the="" this="" to="" triggerall="AILevel>=3;" use="" used="" using="" value="" values="" want="" we="" what=""> Trigger1 = ctrl
Trigger2 = Stateno=1000
Trigger2 = Movehit
value=4000

You can also merge the player and the AI command activation into a single command block, like this:

Type=Changestate
Triggerall=power>=3000
Triggerall=Statetype=S
Triggerall=life<=0.5*Lifemax
Triggerall=ctrl || ((Stateno=1000) && movehit)
Trigger1=var(59)=0; The AI is off
Trigger1=command="super"; and the command in entered
Trigger2=var(59)>0; or the AI is on
Trigger2=abs(P2Bodydist X)<40; and all the AI conditions are true
Trigger2=P2StateType=S
Trigger2=!Inguarddist
Trigger2=random<var(50)*1.1 Trigger2=AILevel>=3
value=4000</var(50)*1.1

If you code your AI using the same merged block for human and AI commands, updating conditions on the attack is easier. For example if you want to reduce the cost from 3000 to 2000, or update the attack so that is also works when used in the air, or want to add it as a combo after different attacks, you only need to change it in one place instead of two. This not just reduced your future work, but also eliminates a possible inconsistency: if you update one of them and forget about the other, you might end up with an attack that works under different conditions for the player than the AI unintentionally.

Common/System behavior

There are a few things a character do without using a move defined in the cmd, and these are:

  • -Jumping
  • -Guarding
  • -Walking

If we want to have control over these the way we have over normal attacks, we have to code for it.

Guarding

To make the AI guard properly, two steps are necessary. The first is a changestate in state -1 to enter the guard states when we want to. This one works well:

[State -1]
Type=Changestate
Triggerall=Ingurddist; Guard when in guard distance
Triggerall=var(59)>0; and the AI is on
Triggerall=ctrl; and we have control
Trigger1 = random< (var(50)*2+(AiLevel>=3)*30); chance is higher than for attacking, but not by too much.
value=120

[State -1]; The engine will still guard by through pressing the back button, we need to disable that.
Type=Assertspecial
Triggerall=StateNo!=[120,160]
Trigger1=var(59)>0
flag=noairguard
flag2=nocrouchguard
flag3=nostandguard

Adding Trigger=!Inguarddist to AI attacks greatly helps against taking unnecessary hits, and increases chance of guarding the attack some ticks later if the guarding chance didn't trigger yet (but still fast enough against slower attacks).. Without that, the character will start to execute an attack, and end up getting hit too often, making the AI to dumb. This makes the AI behave like a player, the slower an attack is, the higher the chance that it will be guarded. Generally, you will want to have this line on all of your AI attacks except light punches or other very fast moves, or moves with invincibility.

The other part of an AI guarding is keeping the AI in the guarding states long enough, and making it go to the appropriate one for each situation. You need to override all the guard states for that. If you don't want to change the conditions on which the AI changes from crouch to stand guarding or back, you can use the code below:

;---------------------------------------------------------------------------
; GUARD (start)
[Statedef 120]
type = U   ;Leave state type unchanged
physics = U;Leave physics unchanged

[State 120]
Type=CtrlSet
Trigger1=var(59)>0
value=0

[State 120, 1]
type = ChangeAnim
trigger1 = Time = 0
value = 120 + (statetype = C) + (statetype = A)*2

[State 120, 2]
type = StateTypeSet
trigger1 = Time = 0 && statetype = S
physics = S

[State 120, 3]
type = StateTypeSet
trigger1 = Time = 0 && statetype = C
physics = C

[State 120, 4]
type = StateTypeSet
trigger1 = Time = 0 && statetype = A
physics = A

[State 120, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59)=0
trigger1 = command = "holddown"
trigger2 = var(59)>0
trigger2 = (abs(P2Dist X)>120) || (P2MoveType=H) || (P2StateType=C)
trigger2 = P2StateType!=A
trigger2 = AILevel>=3
statetype = C
physics = C

[State 120, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59)=0
trigger1 = command!= "holddown"
trigger2 = Var(59) > 0
trigger2 = p2statetype = A
trigger2 = abs(P2BodyDist X)=3
statetype = S
physics = S

[State 120, 5]
type = ChangeState
trigger1 = AnimTime = 0
value = 130 + (statetype = C) + (statetype = A)*2

[State 120, Stop Guarding]
type = ChangeState
trigger1 = command!= "holdback"
trigger1 = var(59)=0
trigger2 =!inguarddist
value = 140

;---------------------------------------------------------------------------
; Stand guard (guarding)
[Statedef 130]
type    = S
physics = S

[State 120]
Type=CtrlSet
Trigger1=var(59)>0
value=0

[State 130, 1]
type = ChangeAnim
trigger1 = Anim!= 130
value = 130

[State 130, Hi to Lo]
type = ChangeState
trigger1 = command = "holddown"
trigger1 = var(59)=0
trigger2 = var(59)>0
trigger2 = (abs(P2Dist X)>120) || (P2MoveType=H) || (P2StateType=C)
trigger2 = P2StateType!=A
trigger2 = AILevel>=3
value = 131

[State 130, Stop Guarding]
type = ChangeState
trigger1 = var(59)=0
trigger1 = command!= "holdback"
trigger2 =!inguarddist
value = 140

;---------------------------------------------------------------------------
; Crouch guard (guarding)
[Statedef 131]
type    = C
physics = C

[State 120]
Type=CtrlSet
Trigger1=var(59)>0
value=0

[State 131, 1]
type = ChangeAnim
trigger1 = Anim!= 131
value = 131

[State 131, Lo to Hi]
type = ChangeState
trigger1 = command!= "holddown"
trigger1 = var(59)=0
trigger2 = Var(59) > 0
trigger2 = p2statetype = A
trigger2 = abs(P2BodyDist X)=3
value = 130

[State 131, Stop Guarding]
type = ChangeState
trigger1 = var(59)=0
trigger1 = command!= "holdback"
trigger2 =!inguarddist
value = 140

;---------------------------------------------------------------------------
; Air guard (guarding)
[Statedef 132]
type    = A
physics = N

[State 120]
Type=CtrlSet
Trigger1=var(59)>0
value=0

[State 132, 1]
type = ChangeAnim
trigger1 = Anim!= 132
value = 132

[State 132, 2]
type = VelAdd
trigger1 = 1
y = Const(movement.yaccel)

[State 132, 3]
type = VarSet
trigger1 = 1
sysvar(0) = (pos y >= 0) && (vel y > 0)

[State 132, 4]
type = VelSet
trigger1 = sysvar(0)
y = 0

[State 132, 5]
type = PosSet
trigger1 = sysvar(0)
y = 0

[State 132, 6]
type = ChangeState
trigger1 = sysvar(0)
trigger1 = command = "holdback" || (var(59)>0)
trigger1 = inguarddist
value = 130

[State 132, 7]
type = ChangeState
trigger1 = sysvar(0)
value = 52

[State 132, Stop Guarding]
type = ChangeState
trigger1 = var(59)=0
trigger1 = command!= "holdback"
trigger2 =!inguarddist
value = 140


;---------------------------------------------------------------------------
; Stand guard hit (shaking)
[Statedef 150]
type    = S
movetype= H
physics = N
velset = 0,0

[State 150, 1]
type = ChangeAnim
trigger1 = 1
value = 150

[State 150, 2]
type = ChangeState
trigger1 = HitShakeOver
value = 151 + 2*(StateType=C)

[State 120, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59)=0
trigger1 = command = "holddown"
trigger2 = var(59)>0
trigger2 = (abs(P2Dist X)>120) || (P2MoveType=H) || (P2StateType=C)
trigger2 = P2StateType!=A
trigger2 = AILevel>=3
statetype = C
physics = C

[State 120, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59)=0
trigger1 = command!= "holddown"
trigger2 = Var(59) > 0
trigger2 = p2statetype = A
trigger2 = abs(P2BodyDist X)=3
statetype = S
physics = S

[State 150, 3]
type = ForceFeedback
trigger1 = time = 0
waveform = square
time = 3

;---------------------------------------------------------------------------
; Stand guard hit (knocked back)
[Statedef 151]
type    = S
movetype= H
physics = S
anim = 150

[State 151, 1]
type = HitVelSet
trigger1 = Time = 0
x = 1

[State 151, 2]
type = VelSet
trigger1 = Time = GetHitVar(slidetime)
trigger2 = HitOver
x = 0

[State 151, 3]
type = CtrlSet
trigger1 = Time = GetHitVar(ctrltime)
trigger1 = var(59)=0
value = 1

[State 120, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59)=0
trigger1 = command = "holddown"
trigger2 = var(59)>0
trigger2 = (abs(P2Dist X)>120) || (P2MoveType=H) || (P2StateType=C)
trigger2 = P2StateType!=A
trigger2 = AILevel>=3
statetype = C
physics = C

[State 120, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59)=0
trigger1 = command!= "holddown"
trigger2 = Var(59) > 0
trigger2 = p2statetype = A
trigger2 = abs(P2BodyDist X)=3
statetype = S
physics = S

[State 151, 4]
type = ChangeState
trigger1 = HitOver
value = 130
ctrl =!var(59)

;---------------------------------------------------------------------------
; Crouch guard hit (shaking)
[Statedef 152]
type    = C
movetype= H
physics = N
velset = 0,0

[State 152, 1]
type = ChangeAnim
trigger1 = 1
value = 151

[State 152, 3]
type = ChangeState
trigger1 = HitShakeOver
value = 151 + 2*(StateType=C)

[State 120, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59)=0
trigger1 = command = "holddown"
trigger2 = var(59)>0
trigger2 = (abs(P2Dist X)>120) || (P2MoveType=H) || (P2StateType=C)
trigger2 = P2StateType!=A
trigger2 = AILevel>=3
statetype = C
physics = C

[State 120, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59)=0
trigger1 = command!= "holddown"
trigger2 = Var(59) > 0
trigger2 = p2statetype = A
trigger2 = abs(P2BodyDist X)=3
statetype = S
physics = S

[State 152, 4]
type = ForceFeedback
trigger1 = time = 0
waveform = square
time = 4

;---------------------------------------------------------------------------
; Crouch guard hit (knocked back)
[Statedef 153]
type    = C
movetype= H
physics = C
anim = 151

[State 153, 1]
type = HitVelSet
trigger1 = Time = 0
x = 1

[State 153, 2]
type = VelSet
trigger1 = Time = GetHitVar(slidetime)
trigger2 = HitOver
x = 0

[State 151, 3]
type = CtrlSet
trigger1 = Time = GetHitVar(ctrltime)
trigger1 = var(59)=0
value = 1

[State 120, Hi to Lo]
type = StateTypeSet
triggerall = statetype = S
trigger1 = var(59)=0
trigger1 = command = "holddown"
trigger2 = var(59)>0
trigger2 = (abs(P2Dist X)>120) || (P2MoveType=H) || (P2StateType=C)
trigger2 = P2StateType!=A
trigger2 = AILevel>=3
statetype = C
physics = C

[State 120, Lo to Hi]
type = StateTypeSet
triggerall = statetype = C
trigger1 = var(59)=0
trigger1 = command!= "holddown"
trigger2 = Var(59) > 0
trigger2 = p2statetype = A
trigger2 = abs(P2BodyDist X)=3
statetype = S
physics = S

[State 153, 4]
type = ChangeState
trigger1 = HitOver
value = 131
ctrl =!var(59)

;---------------------------------------------------------------------------
; Air guard hit (shaking)
[Statedef 154]
type    = A
movetype= H
physics = N
velset = 0,0

[State 154, 1]
type = ChangeAnim
trigger1 = 1
value = 152

[State 154, 2]
type = ChangeState
trigger1 = HitShakeOver
value = 155;AGUARDHIT2

[State 154, 3]
type = ForceFeedback
trigger1 = time = 0
waveform = square
time = 4

;---------------------------------------------------------------------------
; Air guard hit (knocked away)
[Statedef 155]
type    = A
movetype= H
physics = N
anim = 152

[State 155, 1]
type = HitVelSet
trigger1 = Time = 0
x = 1
y = 1

[State 155, 2]
type = VelAdd
trigger1 = 1
y = Const(movement.yaccel)

[State 151, 3]
type = CtrlSet
trigger1 = Time = GetHitVar(ctrltime)
trigger1 = var(59)=0
value = 1

[State 155, 4]
type = VarSet
trigger1 = 1
sysvar(0) = (pos y >= 0) && (vel y > 0)

[State 155, 5]
type = VelSet
trigger1 = sysvar(0)
y = 0

[State 155, 6]
type = PosSet
trigger1 = sysvar(0)
y = 0

[State 155, 6]
type = ChangeState
trigger1 = sysvar(0)
trigger1 = command = "holdback" || var(59)>0
trigger1 = inguarddist
value = 130

[State 155, 7]
type = ChangeState
trigger1 = sysvar(0)
value = 52

Jumping

Making the AI jump is as simple as guarding, just add a changestate to your -1 pointing to the common jump state.

[state -1]
Type=changestate
Trigger1=var(59)>0
Trigger1=your Ai jump conditions here
value=40

The reverse, making the character not jump when the up button is pressed, or at least limiting how ofter that happens (default AI likes to jump way too often, which makes fights annoying) can be done by overriding state 40:

; Jump Start
[Statedef 40]
type    = S
physics = S
anim = 40
ctrl = 0
sprpriority = 1

[State 40]
Type=Changestate
Triggerall=time=0
Triggerall=var(59)>0
Trigger1=random>var(50); Add your limiting condition to AI jumping here
value=0; Change back to standing immediately when the AI would start a jump and it is not necessary.
ctrl=1

[State 40, 1]
type = VarSet
trigger1 = Time = 0
sysvar(1) = 0

[State 40, 2]
type = VarSet
trigger1 = command = "holdfwd"
trigger1 = var(59)=0
trigger2 = var(59)>0
trigger2 = condition when the AI needs to jump forward
sysvar(1) = 1

[State 40, 3]
type = VarSet
trigger1 = command = "holdback"
trigger1 = var(59)=0
trigger2 = var(59)>0
trigger2 = condition when the AI needs to jump back
sysvar(1) = -1

[State 40, 4]
type = VelSet
trigger1 = AnimTime = 0
x = ifelse(sysvar(1)=0, const(velocity.jump.neu.x), ifelse(sysvar(1)=1, const(velocity.jump.fwd.x), const(velocity.jump.back.x)))
y = const(velocity.jump.y)

[State 40, 5]
type = VelSet
trigger1 = AnimTime = 0
trigger1 = prevstateno = 100;RUN_FWD
trigger1 = sysvar(1) = 1
x = const(velocity.runjump.fwd.x)

[State 40, 6]
type = ChangeState
trigger1 = AnimTime = 0
value = 50
ctrl = 1

Note, that if you do not want your standing animation to get restarted from frame 0, you need to change your anim=40 line into a changeanim placed below the changestate to state 0

Walking

Usually, there is no need to code for walking for two reasons. If you want your AI to move closer/further, you'll use the running moves instead, as most characters have those. Walking is less optimal than running. The walking done by the default AI doesn't hurt character performance either, so there is no need to disable it usually. At least the character does something when idle for a long time at low difficulty levels instead of just standing.

This guide was created by Seravy.

Link to comment
Share on other sites

  • 2 months later...
  • 6 months later...

Not sure if I can properly utilize this detorial, but I know there is a few characters that deserve the love they're creaters never were able to work in... *points finger at the robot masters from megaman2...

Going to try reworking crashman, and if I'm successful, metalman, though pulling it off might be hard.

Link to comment
Share on other sites

Something that this guide never addresses is that using random in your AI triggers is bad coding practice. The reason for this for two reasons;

  1. Mugen processes states in top-down order, including the negative states (-3, -2, and -1)
  2. Random returns a different value every time it is called

Take the following code into consideration:


[State -1, Shoryuken]

type = changestate

trigger1 = AILevel

trigger1 = ctrl

trigger1 = random<300

 

[State -1, Tatsumaki]

type = changestate

trigger1 = AILevel

trigger1 = ctrl

trigger1 = random<300

 

Looking at each AI changestate at face value, you would think that each one has a 30% chance at activating (random<300 roughly equates to 30% chance). However, because mugen processes states in top-down order, if the first changestate occurs, the second changestate will never process. Thus, the second changestate in actuality only has a 21% chance of occuring (70% chance of the 1st changestate not occuring, plus the 30% chance that the 2nd changestate will occur). When you have multiple AI changestates with similar or identical trigger conditions, you will need to account for this. A common way to do this (and much better practice) is to instead store the result of random into a variable, and compare the variable to different ranges of values in order to determine which changestate should occur.

Link to comment
Share on other sites

A method I do for chain combo AI, is I have a varrandom, randomize a variable for me within the range of 100 or so.

and if the variable lands on a number range betwen

0 - 24

25 - 49

50 - 74

75 - 100

it will trigger different states.

I know it's not a perfect 1/4 chance, but do you think this is a good method for coding AI CHAIN COMBO RANDOMNESS ?

Link to comment
Share on other sites

  • 6 months later...
  • 4 weeks later...
  • 1 year later...
  • 2 years later...

HI! This thread seems super useful, and though its a few years old hopefully I can get a response or pointed to a new more current thread. I have attempted to input the concept behind the code into my game starting with Kung Fu Man (720p version). It appears I am missing something because it does not effect the way he plays. Per example, I tried to stop him from spamming his taunt (bow) as an AI by doing the following: 

[State -1, Taunt]

type = ChangeState

value = 195

triggerall = var(59)>0 ; use only if AI is on

triggerall =abs(P2Bodydist X)>50 ; only taunts when enemy is farther

triggerall = life>=.05*Lifemax ; only taunts if over 50% life

trigger1 = statetype != A

trigger1 = var(59)>0 ; use only is AI is on

 

My confusion comes in a bit as to where to place the var(59) check, in the trigger, or in the triggerall as I notice in the original post it switches between the two. 

 

Though it is also possible I am not placing the AI activation code in the correct section. It notes to put it in one of the minus states. Can I get a little more clarity then that? 

Link to comment
Share on other sites

  • 2 months later...

What RicePigeon said. AILevel is such a blessing of a condition. You no longer have to put dumb commands at the top of a CMD file, and you can even make an AI behave differently depending on the difficulty setting.

 

Here's another tutorial regarding guarding that I found.

https://mugenmultiverse.forumotion.com/t7611-shirayuki33desu-s-way-to-make-your-mugen-character-s-a-i-block-guard-better

https://archive.is/Eh9T7

It's with winmugen in mind, but that just means to replace var(59) stuff with AILevel.

Link to comment
Share on other sites

  • 2 years later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...