Flying: Difference between revisions
Icecream17 (talk | contribs) No edit summary |
Icecream17 (talk | contribs) (→update_flying: explain best flying) |
||
(6 intermediate revisions by the same user not shown) | |||
Line 22: | Line 22: | ||
As with all airborne actions, a variety of "cancels" are checked prior to actually performing any airborne action. See [[Jump#Airborne cancels]]. | As with all airborne actions, a variety of "cancels" are checked prior to actually performing any airborne action. See [[Jump#Airborne cancels]]. | ||
Then: | Then:<ref>https://github.com/n64decomp/sm64/blob/66018e9f3caaa67399218971d61366cb3f7ba7d7/src/game/mario_actions_airborne.c#L1726</ref> | ||
# If Z is pressed | # If Z is pressed | ||
## If Mario's camera mode is CAMERA_MODE_BEHIND_MARIO, set_camera_mode to m->area->camera->defMode | ## If Mario's camera mode is CAMERA_MODE_BEHIND_MARIO, set_camera_mode to m->area->camera->defMode | ||
Line 37: | Line 37: | ||
### Set action state to 1 | ### Set action state to 1 | ||
# Call '''<code>update_flying</code>''' | # Call '''<code>update_flying</code>''' | ||
# switch [[Movement_steps#Perform_Air_Step]]: | # switch [[Movement_steps#Perform_Air_Step]]: (Note: if there are no floor/wall/ceiling/oob interactions this just '''adds vel[0,1,2] to pos[0,1,2]'''.) | ||
#* air step none: | #* air step none: | ||
#*# Update graphics (camera) angle to be behind Mario | #*# Update graphics (camera) angle to be behind Mario | ||
Line 55: | Line 55: | ||
# finally, play the flying sound (adjust sound for speed) | # finally, play the flying sound (adjust sound for speed) | ||
Notice that there is no code handling air steps for ledge grabbing or hanging on a ceiling, so such transitions are impossible. | Notice that there is no code handling air steps for ledge grabbing or hanging on a ceiling, so such transitions are impossible. | ||
===update_flying=== | |||
Casually, tilt the joystick left to move left, right to move right, up to move down, down to move up. '''TODO:''' I heard moving down then up can be faster then just the direct line. Explain big picture movements, what happens in a practical example. | |||
Beyond this, the pseudocode is provided but probably isn't as useful as the <code>update_flying</code> code itself: | |||
# Joystick inputs affect (pitch,yaw) velocity. | |||
# Yaw velocity affects yaw, roll, and forward velocity. | |||
# Pitch affects forward velocity. | |||
# Forward velocity becomes at least 0 | |||
# Forward velocity affects pitch (see below table) | |||
# Pitch velocity affects pitch. | |||
# Pitch is clamped to be inside [-0x2AAA, 0x2AAA] | |||
# x,y,z velocity is more or less directly exactly trigonometrically correspondent to forward velocity, pitch, and yaw. | |||
# xz sliding velocity updated to correspond | |||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
void update_flying(struct MarioState *m) { | |||
UNUSED u8 filler[4]; | |||
update_flying_pitch(m); | |||
update_flying_yaw(m); | |||
m->forwardVel -= 2.0f * ((f32) m->faceAngle[0] / 0x4000) + 0.1f; | |||
m->forwardVel -= 0.5f * (1.0f - coss(m->angleVel[1])); | |||
if (m->forwardVel < 0.0f) { | |||
m->forwardVel = 0.0f; | |||
} | |||
if (m->forwardVel > 16.0f) { | |||
m->faceAngle[0] += (m->forwardVel - 32.0f) * 6.0f; | |||
} else if (m->forwardVel > 4.0f) { | |||
m->faceAngle[0] += (m->forwardVel - 32.0f) * 10.0f; | |||
} else { | |||
m->faceAngle[0] -= 0x400; | |||
} | |||
m->faceAngle[0] += m->angleVel[0]; | |||
if (m->faceAngle[0] > 0x2AAA) { | |||
m->faceAngle[0] = 0x2AAA; | |||
} | |||
if (m->faceAngle[0] < -0x2AAA) { | |||
m->faceAngle[0] = -0x2AAA; | |||
} | |||
m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]); | |||
m->vel[1] = m->forwardVel * sins(m->faceAngle[0]); | |||
m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]); | |||
m->slideVelX = m->vel[0]; | |||
m->slideVelZ = m->vel[2]; | |||
} | |||
</syntaxhighlight> | |||
'''Key takeaways''': | |||
* Pitch velocity ''additively'' affects forward velocity, but ''sin'' effects pitch. '''The most energy efficient flying is to fly a lot down, and then a little up''', as, for example, sin(-0.5) + sin(0.25) + sin(0.25) ≈ 0.0153823799048 (positive), while the totally additive effect is still 0 (actually -0.3 because of a flat -0.1 per trame) | |||
* Pitching to move up happens faster with higher forward velocity. | |||
** The difference between 4 and 5 speed, and 16 and 17 speed is especially large. | |||
* At the same time, forward velocity is based on pitch. (But it is also based on:) | |||
** There is a base 0.1 forwardVel speed loss | |||
** There is anywhere from 0 to 0.5 further speed loss - more speed is lost the higher the yaw velocity is (ie '''the more Mario turns'''). | |||
* Pitch has a minimum and maximum and cannot overflow with standard speeds. | |||
* '''More below the tables''' | |||
{| class="wikitable" | |||
|+forwardVel's affect on pitch | |||
|'''<=4''' | |||
|5 | |||
|6 | |||
|7 | |||
|8 | |||
|9 | |||
|10 | |||
|11 | |||
|12 | |||
|13 | |||
|14 | |||
|15 | |||
|'''16''' | |||
|17 | |||
|18 | |||
|19 | |||
|... | |||
|31 | |||
|32 | |||
|33 | |||
|- | |||
| -1024 | |||
| -270 | |||
| -260 | |||
| -250 | |||
| -240 | |||
| -230 | |||
| -220 | |||
| -210 | |||
| -200 | |||
| -190 | |||
| -180 | |||
| -170 | |||
| -160 | |||
| -90 | |||
| -84 | |||
| -76 | |||
|... | |||
| -6 | |||
|0 | |||
|6 | |||
|} | |||
<hr> | |||
Here are some tables summarizing the <code>approach_s32</code>, <code>update_flying_pitch</code>, and <code>update_flying_yaw</code> functions. | |||
{| class="wikitable" | |||
|+ | |||
update_flying_pitch (target vel = -(stickX * (forwardVel / 5))) | |||
! | |||
!joystick is down (move up) | |||
!joystick is up (move down) | |||
!joystick is neutral | |||
|- | |||
|negative pitch velocity | |||
|add 64 vel, cap at 32 | |||
| approach (at most 64 up or 32 down) | |||
|approach 0 (by at most 64) | |||
|- | |||
|positive pitch velocity | |||
| approach (at most 32 up or 64 down) | |||
|subtract 64 vel, cap at -32 | |||
|approach 0 (by at most 64) | |||
|- | |||
|zero pitch velocity | |||
| approach (at most 32 up or 64 down) | |||
| approach (at most 64 up or 32 down) | |||
| approach 0 (by at most 64) | |||
|} | |||
{| class="wikitable" | |||
|+update_flying_yaw (target vel = -(stickY * (forwardVel / 4))) | |||
! | |||
!joystick is left | |||
!joystick is right | |||
!joystick is neutral | |||
|- | |||
|negative yaw velocity | |||
| add 64 vel, cap at 16 | |||
|approach (at most 32 up or 16 down) | |||
|approach 0 (by at most 64) | |||
|- | |||
|positive yaw velocity | |||
| approach (at most 16 up or 32 down) | |||
| subtract 64 vel, cap at -16 | |||
|approach 0 (by at most 64) | |||
|- | |||
|zero yaw velocity | |||
|approach (at most 16 up or 32 down) | |||
|approach (at most 32 up or 16 down) | |||
|approach 0 (by at most 64) | |||
|} | |||
'''Key takeaways''': | |||
# Many joystick positions are equivalent. | |||
# One cannot change velocity by more than 64 (ignoring the effects of speed) | |||
# Holding joystick x=0 or y=0 will approach the corresponding 0 by at most 64. | |||
#* In contrast, holding the opposite direction changes the angle by 32 (yaw) or 64 (pitch), unconditionally | |||
#* In further contrast, holding less of the same direction changes the angle by 32 (yaw) or 64 (pitch) | |||
#* In even furthest contrast, holding more of the same direction changes the angle by 16 (yaw) or 32 (pitch) | |||
#* So it might be faster to say, modify pitch:as such: -56 to -4 to 60 instead of -56 to 8 to 40. Notice the slightly under zero instead of slightly over zero. | |||
After that, update_flying_yaw does: | |||
#Add yaw velocity to yaw | |||
# Set roll to yaw times negative twenty (-20) | |||
'''Note that turning left = positive yaw''' (up = positive pitch).<ref>https://youtu.be/TQt8MCsniQI</ref><syntaxhighlight lang="c"> | |||
/** | /** | ||
* Return the value 'current' after it tries to approach target, going up at | * Return the value 'current' after it tries to approach target, going up at | ||
Line 133: | Line 296: | ||
m->angleVel[0] = approach_s32(m->angleVel[0], 0, 0x40, 0x40); | m->angleVel[0] = approach_s32(m->angleVel[0], 0, 0x40, 0x40); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==References== | |||
<references /> | |||
{{Actions}} | {{Actions}} |
Latest revision as of 16:37, 2 August 2024
Flying | |
Properties | |
Hex | 0x10880899 |
Action Flags | air, diving, attacking, swimming/flying |
Action Group | Airborne |
ID | 0x099 |
Transitions | |
Into | Airborner cancels: Water Plunge, Squished, Vertical Wind (theoretically), (Technically, because of the flying triple jump code): Double Jump Land, Lava Boost (theoretically), non cancel: Ground Pound, Freefall, Dive Slide, Backwards Air Kb, Lava Boost (again) (theoretically) |
Out of | Shot From Cannon, Flying Triple Jump, when spawning in some levels like Tower of the Wing Cap |
Other | |
Animation | 0x5B fly from cannon, 0xCF forwards spinning flip, 0x29 wing cap fly |
Flying is an action that can occurs when Mario triple jumps or does a cannon shot while wearing the wing cap.
Entering flying
- Cannon shot: when y vel < 0 (and Mario does not cancel) (Action argument: 0)
- Flying Triple Jump: when y vel < 4 (and Mario does not cancel, dive, or ground pound) (Action argument: 1)
- Spawning in, e.g, Tower of the Wing Cap: (Action argument: 2)
Behavior
As with all airborne actions, a variety of "cancels" are checked prior to actually performing any airborne action. See Jump#Airborne cancels.
Then:[1]
- If Z is pressed
- If Mario's camera mode is CAMERA_MODE_BEHIND_MARIO, set_camera_mode to m->area->camera->defMode
- Ground Pound
- If Mario is not wearing the wing cap
- If Mario's camera mode is CAMERA_MODE_BEHIND_MARIO, set_camera_mode to m->area->camera->defMode
- Freefall
- If Mario's camera mode is not CAMERA_MODE_BEHIND_MARIO, set_camera_mode to CAMERA_MODE_BEHIND_MARIO
- If the action state is 0
- If the action argument is 0 (was shot from cannon), set Mario's animation to MARIO_ANIM_FLY_FROM_CANNON, else set Mario's animation to MARIO_ANIM_FORWARD_SPINNING_FLIP
- If the animation finished,
- If the action argument is 2 (spawned in), load_level_init_text and set action argument to 1
- Set Mario's animation to MARIO_ANIM_WING_CAP_FLY
- Set action state to 1
- Call
update_flying
- switch Movement_steps#Perform_Air_Step: (Note: if there are no floor/wall/ceiling/oob interactions this just adds vel[0,1,2] to pos[0,1,2].)
- air step none:
- Update graphics (camera) angle to be behind Mario
- Set action timer to 0
- air step land:
- Set action to Dive Slide
- Set animation to MARIO_ANIM_DIVE
- Set animation frame to 7
- Set facing angle (x) to 0
- Set camera mode to m->area->camera->defMode
- air step hit wall:
- todo
- if wall is not null, stuff, Backwards Air Kb
- else, stuff (but no knockback, this is probably out of bounds)
- air step hit lava wall:
- stop holding, stop riding, Lava Boost
- air step none:
- finally, play the flying sound (adjust sound for speed)
Notice that there is no code handling air steps for ledge grabbing or hanging on a ceiling, so such transitions are impossible.
update_flying
Casually, tilt the joystick left to move left, right to move right, up to move down, down to move up. TODO: I heard moving down then up can be faster then just the direct line. Explain big picture movements, what happens in a practical example.
Beyond this, the pseudocode is provided but probably isn't as useful as the update_flying
code itself:
- Joystick inputs affect (pitch,yaw) velocity.
- Yaw velocity affects yaw, roll, and forward velocity.
- Pitch affects forward velocity.
- Forward velocity becomes at least 0
- Forward velocity affects pitch (see below table)
- Pitch velocity affects pitch.
- Pitch is clamped to be inside [-0x2AAA, 0x2AAA]
- x,y,z velocity is more or less directly exactly trigonometrically correspondent to forward velocity, pitch, and yaw.
- xz sliding velocity updated to correspond
void update_flying(struct MarioState *m) {
UNUSED u8 filler[4];
update_flying_pitch(m);
update_flying_yaw(m);
m->forwardVel -= 2.0f * ((f32) m->faceAngle[0] / 0x4000) + 0.1f;
m->forwardVel -= 0.5f * (1.0f - coss(m->angleVel[1]));
if (m->forwardVel < 0.0f) {
m->forwardVel = 0.0f;
}
if (m->forwardVel > 16.0f) {
m->faceAngle[0] += (m->forwardVel - 32.0f) * 6.0f;
} else if (m->forwardVel > 4.0f) {
m->faceAngle[0] += (m->forwardVel - 32.0f) * 10.0f;
} else {
m->faceAngle[0] -= 0x400;
}
m->faceAngle[0] += m->angleVel[0];
if (m->faceAngle[0] > 0x2AAA) {
m->faceAngle[0] = 0x2AAA;
}
if (m->faceAngle[0] < -0x2AAA) {
m->faceAngle[0] = -0x2AAA;
}
m->vel[0] = m->forwardVel * coss(m->faceAngle[0]) * sins(m->faceAngle[1]);
m->vel[1] = m->forwardVel * sins(m->faceAngle[0]);
m->vel[2] = m->forwardVel * coss(m->faceAngle[0]) * coss(m->faceAngle[1]);
m->slideVelX = m->vel[0];
m->slideVelZ = m->vel[2];
}
Key takeaways:
- Pitch velocity additively affects forward velocity, but sin effects pitch. The most energy efficient flying is to fly a lot down, and then a little up, as, for example, sin(-0.5) + sin(0.25) + sin(0.25) ≈ 0.0153823799048 (positive), while the totally additive effect is still 0 (actually -0.3 because of a flat -0.1 per trame)
- Pitching to move up happens faster with higher forward velocity.
- The difference between 4 and 5 speed, and 16 and 17 speed is especially large.
- At the same time, forward velocity is based on pitch. (But it is also based on:)
- There is a base 0.1 forwardVel speed loss
- There is anywhere from 0 to 0.5 further speed loss - more speed is lost the higher the yaw velocity is (ie the more Mario turns).
- Pitch has a minimum and maximum and cannot overflow with standard speeds.
- More below the tables
<=4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ... | 31 | 32 | 33 |
-1024 | -270 | -260 | -250 | -240 | -230 | -220 | -210 | -200 | -190 | -180 | -170 | -160 | -90 | -84 | -76 | ... | -6 | 0 | 6 |
Here are some tables summarizing the approach_s32
, update_flying_pitch
, and update_flying_yaw
functions.
joystick is down (move up) | joystick is up (move down) | joystick is neutral | |
---|---|---|---|
negative pitch velocity | add 64 vel, cap at 32 | approach (at most 64 up or 32 down) | approach 0 (by at most 64) |
positive pitch velocity | approach (at most 32 up or 64 down) | subtract 64 vel, cap at -32 | approach 0 (by at most 64) |
zero pitch velocity | approach (at most 32 up or 64 down) | approach (at most 64 up or 32 down) | approach 0 (by at most 64) |
joystick is left | joystick is right | joystick is neutral | |
---|---|---|---|
negative yaw velocity | add 64 vel, cap at 16 | approach (at most 32 up or 16 down) | approach 0 (by at most 64) |
positive yaw velocity | approach (at most 16 up or 32 down) | subtract 64 vel, cap at -16 | approach 0 (by at most 64) |
zero yaw velocity | approach (at most 16 up or 32 down) | approach (at most 32 up or 16 down) | approach 0 (by at most 64) |
Key takeaways:
- Many joystick positions are equivalent.
- One cannot change velocity by more than 64 (ignoring the effects of speed)
- Holding joystick x=0 or y=0 will approach the corresponding 0 by at most 64.
- In contrast, holding the opposite direction changes the angle by 32 (yaw) or 64 (pitch), unconditionally
- In further contrast, holding less of the same direction changes the angle by 32 (yaw) or 64 (pitch)
- In even furthest contrast, holding more of the same direction changes the angle by 16 (yaw) or 32 (pitch)
- So it might be faster to say, modify pitch:as such: -56 to -4 to 60 instead of -56 to 8 to 40. Notice the slightly under zero instead of slightly over zero.
After that, update_flying_yaw does:
- Add yaw velocity to yaw
- Set roll to yaw times negative twenty (-20)
Note that turning left = positive yaw (up = positive pitch).[2]
/**
* Return the value 'current' after it tries to approach target, going up at
* most 'inc' and going down at most 'dec'.
*/
s32 approach_s32(s32 current, s32 target, s32 inc, s32 dec) {
//! If target is close to the max or min s32, then it's possible to overflow
// past it without stopping.
if (current < target) {
current += inc;
if (current > target) {
current = target;
}
} else {
current -= dec;
if (current < target) {
current = target;
}
}
return current;
}
void update_flying_yaw(struct MarioState *m) {
s16 targetYawVel = -(s16)(m->controller->stickX * (m->forwardVel / 4.0f));
if (targetYawVel > 0) {
if (m->angleVel[1] < 0) {
m->angleVel[1] += 0x40;
if (m->angleVel[1] > 0x10) {
m->angleVel[1] = 0x10;
}
} else {
m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x10, 0x20);
}
} else if (targetYawVel < 0) {
if (m->angleVel[1] > 0) {
m->angleVel[1] -= 0x40;
if (m->angleVel[1] < -0x10) {
m->angleVel[1] = -0x10;
}
} else {
m->angleVel[1] = approach_s32(m->angleVel[1], targetYawVel, 0x20, 0x10);
}
} else {
m->angleVel[1] = approach_s32(m->angleVel[1], 0, 0x40, 0x40);
}
m->faceAngle[1] += m->angleVel[1];
m->faceAngle[2] = 20 * -m->angleVel[1];
}
void update_flying_pitch(struct MarioState *m) {
s16 targetPitchVel = -(s16)(m->controller->stickY * (m->forwardVel / 5.0f));
if (targetPitchVel > 0) {
if (m->angleVel[0] < 0) {
m->angleVel[0] += 0x40;
if (m->angleVel[0] > 0x20) {
m->angleVel[0] = 0x20;
}
} else {
m->angleVel[0] = approach_s32(m->angleVel[0], targetPitchVel, 0x20, 0x40);
}
} else if (targetPitchVel < 0) {
if (m->angleVel[0] > 0) {
m->angleVel[0] -= 0x40;
if (m->angleVel[0] < -0x20) {
m->angleVel[0] = -0x20;
}
} else {
m->angleVel[0] = approach_s32(m->angleVel[0], targetPitchVel, 0x40, 0x20);
}
} else {
m->angleVel[0] = approach_s32(m->angleVel[0], 0, 0x40, 0x40);
}
}