-
Notifications
You must be signed in to change notification settings - Fork 69
Add climbing #1266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add climbing #1266
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of doing 4 more collision checks per frame, could you maybe reuse the code from block touch detection added in #1143?
Climbing on blocks with collision works pretty good. Blocks without collision on the other hand... they start i climb when passing through. Should the player be able to walk through without obstruction? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you misunderstood me. I want you to reuse the intersection results from there, by e.g. adding a touch callback for climbable blocks or something like that.
What you have done here is better than before, but it's still one extra pass that iterates through all blocks in the player's hitbox.
Also sorry for the late review, I had to focus on the new network system.
I didn't do this because I felt like it would be too restrictive. I'm using a smaller hitbox to prevent the player from skipping gaps. |
If it's too restrictive, then please improve the interface of touch events. We can't keep adding a new collision test call for every single addition to the physics system. These are not cheap at all!
According to the code you are using a larger hitbox, also what do you mean by skipping gaps? |
src/game.zig
Outdated
} | ||
} else break :isClimbing false; | ||
}; | ||
const footHitbox: collision.Box = .{.min = -Player.outerBoundingBoxExtent, .max = Player.outerBoundingBoxExtent*Vec3d{1, 1, -0.1}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the code you are using a larger hitbox
I did use a smaller hitbox unless I'm missing something.
what do you mean by skipping gaps?
In Minecraft it used to be possible to climb ladders where every other block is a ladder block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I see, I was looking at the wrong thing.
In Minecraft it used to be possible to climb ladders where every other block is a ladder block.
In my mind this should be possible, but slower. Mabye we could have a climbing speed parameter that gets averaged of the entire area of the player hitbox in the movement direction. Then you could also assign different climbing speeds to different climbable blocks. E.g. ivy could have a slower climbing speed.
Take a look at the ice code for an example of this averaging on a different axis.
src/game.zig
Outdated
// TODO Find a way to prevent the entity to skip gaps | ||
if(block.?.climbable() and !entity.climbing) { | ||
// Prevent the entity from skipping gaps | ||
const isBelowTop = posZ < maxZ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now i kind of ignore the top blocks which feels cheaty
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also could you make ivy climbable as an example to make testing easier?
src/blocks.zig
Outdated
@@ -155,6 +157,8 @@ pub fn register(_: []const u8, id: []const u8, zon: ZonElement) u16 { | |||
_absorption[size] = zon.get(u32, "absorbedLight", 0xffffff); | |||
_degradable[size] = zon.get(bool, "degradable", false); | |||
_selectable[size] = zon.get(bool, "selectable", true); | |||
_climbable[size] = zon.get(bool, "climbable", false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be implicit (climbSpeed == 0), so we don't need to set two parameters to make something climbable.
src/game.zig
Outdated
const maxZ: i32 = @intFromFloat(@floor(boundingBox.max[2])); | ||
|
||
var climbSpeed: f64 = 0; | ||
var totalArea: f64 = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should be f32, since you cast them anyways and there is not much precision to be gained here.
src/game.zig
Outdated
return defaultSpeed; | ||
} | ||
|
||
return @floatCast(climbSpeed/totalArea); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would only divide by the totalArea if the totalArea is bigger than the player's hitbox area in that direction.
As it is right now we get the same block skipping problem that you described.
src/game.zig
Outdated
@@ -904,6 +1002,9 @@ pub fn update(deltaTime: f64) void { // MARK: update() | |||
movementSpeed = @max(movementSpeed, 5.5); | |||
movementDir[2] += 5.5; | |||
} | |||
} else if(Player.super.climbing) { | |||
movementSpeed = @max(movementSpeed, Player.currentClimbSpeed*0.7); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels wrong. When jumping next to a climbable block you should be able to keep your jumping speed, slowly slowing down to the ladder speed due to gravity.
Furthermore using @min
and @max
in collision code is always bad, since it has the ability to cut your velocity instantly as soon as you touch a certain block.
It's better to use friction to slow it down slowly instead.
src/game.zig
Outdated
@@ -904,6 +1002,9 @@ pub fn update(deltaTime: f64) void { // MARK: update() | |||
movementSpeed = @max(movementSpeed, 5.5); | |||
movementDir[2] += 5.5; | |||
} | |||
} else if(Player.super.climbing) { | |||
movementSpeed = @max(movementSpeed, Player.currentClimbSpeed*0.7); | |||
movementDir += up*@as(Vec3d, @splat(walkingSpeed)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
movementDir
is normalized.
This has the side effect off slowing you down even if you just walk alongside a climbable block.
Up direction should be handled separately here.
src/game.zig
Outdated
@@ -1010,6 +1111,10 @@ pub fn update(deltaTime: f64) void { // MARK: update() | |||
move[i] = a/frictionCoefficient*deltaTime - c_1/frictionCoefficient*@exp(-frictionCoefficient*deltaTime) + c_1/frictionCoefficient; | |||
} | |||
|
|||
if(Player.super.climbing and Player.crouching and move[2] < 0) { | |||
move[2] = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is bad since it allows you to negate any fall damage by jumping onto a ladder. There should be a velocity limit beyond which you cannot hold onto the climbable block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed it to use a slip velocity of around 3 blocks for now.
src/game.zig
Outdated
|
||
// climbing | ||
if(block.?.climbable() and !entity.climbing) { | ||
const touchX: bool = isBlockIntersecting(block.?, posX, posY, posZ, center, extendClimableX); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason why you cannot use the touchX
defined below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using a different extend (climbAmount
) than the original touch extend of 0.01
. I could reuse this when I use the same extend. But this makes climbing down really hard.
src/game.zig
Outdated
|
||
var climbAmount: f32 = 0.01; | ||
if(!Player.onGround or KeyBoard.key("jump").pressed) { | ||
climbAmount = 0.4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels weird in my opinion. Why can you suddenly grab climbable blocks 0.4 meters away?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made it this big as a QOL feature. With an amount of 0.01
its incredibly difficult to grab onto blocks. It could be smaller of course.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, you just have to walk right into it until you hit the hitbox, and then you can just climb.
I don't see how that's difficult.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Climbing up isn't as much of a problem, but trying to get down from higher elevations.
2025-05-02_22-06-15_001.mp4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if you could crouch off the edge into the ladder? I think that would be a good fix
I was skeptical about this PR at first, but it looks to be better than Minecraft's janky climbing (plus, you added slip velocity! No ladder clutches XD) |
Also, I'm glad you made ladder movement different from regular movement. That's always been a pet-peeve of mine. (Source game ladders are the bane of my existence) |
Being by first PR I also expected this to go differently (my math is rusty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2025-05-02_17-16-23.mp4
Found a bug!
Fixed |
Oops, I love github. I was trying to get rid of my review XD |
It is now possible to crouch off of blocks onto climbable blocks. Took me longer than I want to admit. You need to crouch backwards towards the edge and let go of crouch to go down. It's still a little death trap. |
Climbing gets really slow when you reach the top of the block? |
What if crouching on a climbable made you go down, and not having any inputs kept you still? |
It gets slower because it is based on the area of the collision, so you get slowed down with air gaps. I could increase the default climb speed to make it a little faster. How do i differentiate between gaps and platforms without blasting collision checks |
Yeah, that's exactly why I suggested that crouching on a climbable makes you go down. This is an issue in Minecraft as well XD You could even have the (backwards key) make you climb down. We'll have to workshop it. |
You could restrict the climbing collision box to the legs, however that would break the climbing on the ceiling thing. The alternative would be to detect if there is a ledge, but that is indeed rather complicated (though a nice byproduct of this would be that you would be able to make your own ladders by stacking slabs or fences) |
Yes please, that would be great. This has always bothered me in minecraft. |
Actually instead of increasing the climb speed, you could change the interpolation. e.g. a weighted pythagorean sum is always closer to the largest element than just a weighted sum. |
The area detection doesn't seem to work correctly. I can climb on the side of an ivy covered pillar: video-2025-05-09_09.31.28.mp4 |
@@ -15,6 +15,8 @@ health: f32 = 8, | |||
maxHealth: f32 = 8, | |||
energy: f32 = 8, | |||
maxEnergy: f32 = 8, | |||
climbing: bool = false, | |||
touchingClimbable: bool = false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This Data should only be stored in the client-side player struct for now.
src/game.zig
Outdated
@@ -509,6 +509,12 @@ pub const collision = struct { | |||
|
|||
entity.touchingClimbable = entity.touchingClimbable or isSideBlock or isBottomBlock; | |||
entity.climbing = isSideBlock and (!isBottomBlock or isFullBlock); | |||
|
|||
const normal = if (model.internalQuads.len > 0) model.internalQuads[0].quadInfo().normal else break :missingNormal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, so you cannot make full blocks climbable? That doesn't make sense.
Also does this work on other blocks, like e.g. if you were to make a climbable pole?
This pull request adds the
climbable: bool
block trait, block detection for climbable blocks and player input handeling.Related to: #1031