How can I make my player fall through a one-way platform with an input?
Asked Answered
C

7

0

I'm currently making a platformer game where I want the player to not collide with a platform made from a tile in a tilemap when they hold the down arrow, but to still be able to collide with other tiles. I thought that I could make the platform tile its own physics layer, and to make the player turn its physics mask for that layer on and off, depending on the input. However, I could not find a way to do that. Some answers said to just increase the y-coordinate for the player when they press the button and are on the floor, but the player's y velocity keeps resetting every time they hit a platform. Could anybody please help?

Constitutional answered 3/11, 2023 at 19:28 Comment(0)
Z
0

Constitutional I thought that I could make the platform tile its own physics layer, and to make the player turn its physics mask for that layer on and off, depending on the input. However, I could not find a way to do that.

This way works! You just have to know how to use the bitmasks. See here for an explanation.

I wrote some simple modifications to the default CharacterBody2D script to show how it can be done:

extends CharacterBody2D

const SPEED = 300.0
const JUMP_VELOCITY = -800.0

#switch back to this when we aren't holding down
var collision_mask_default := collision_mask

#This is a small trick with bits. On the right side we have the binary representation of the bitmask for layer 3.
#This assumes that we are putting our one-way platforms in layer 3. See the explanation in the docs.
#The ~ flips all the bits so all the 0s become 1s and all the 1s become 0s,
#becoming basically "every layer except for 3"
#The & gives a result where each bit is a 1 if and only if both sides are 1, otherwise 0
#The result is, if collision_mask were 0b00000000_00000000_00000000_00001111,
#then collision_mask_fallthrough would be 0b00000000_00000000_00000000_00001011
var collision_mask_fallthrough = collision_mask & ~0b00000000_00000000_00000000_00000100

# Get the gravity from the project settings to be synced with RigidBody nodes.
var gravity: int = ProjectSettings.get_setting("physics/2d/default_gravity")

func _physics_process(delta: float) -> void:
	if Input.is_action_pressed("ui_down"):
		#if we're holding down, apply the collision mask to not collide with the one-way platforms
		collision_mask = collision_mask_fallthrough
	else:
		#make sure we are colliding with them otherwise
		collision_mask = collision_mask_default

	# Add the gravity.
	if not is_on_floor():
		velocity.y += gravity * delta

	# Handle jump.
	if Input.is_action_just_pressed("ui_accept") and is_on_floor():
		velocity.y = JUMP_VELOCITY

	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	var direction := Input.get_axis("ui_left", "ui_right")
	if direction:
		velocity.x = direction * SPEED
	else:
		velocity.x = move_toward(velocity.x, 0, SPEED)

	move_and_slide()
Zarah answered 3/11, 2023 at 21:35 Comment(0)
Z
0

Btw, I prefer to add something like this to my project:

class_name Layers

enum {
	PLAYER = 0b00000000_00000000_00000000_00000001,
	STAGE = 0b00000000_00000000_00000000_00000010,
	PLATFORM = 0b00000000_00000000_00000000_00000100,
	ENEMY = 0b00000000_00000000_00000000_00001000,
}

That way,
var collision_mask_fallthrough = collision_mask & ~0b00000000_00000000_00000000_00000100
becomes
var collision_mask_fallthrough = collision_mask & ~Layers.PLATFORM

which is much easier to read.

Zarah answered 3/11, 2023 at 21:49 Comment(0)
R
0

Zarah Talking about readability, personally I would only write as many nibbles or bytes as needed.

enum {
	PLAYER = 0b0001,
	STAGE = 0b0010,
}

Or:

enum {
	PLATFORM = 0b00000100,
	ENEMY = 0b00001000,
}

Otherwise it looks like there might be bits set in the upper bytes. Just a minor nitpick though. 😉

Ruscher answered 4/11, 2023 at 10:25 Comment(0)
Z
0

Ruscher Fair criticism. I actually use 2n to write mine and didn't know you could just do nibbles, but I thought dragging powers of 2 into the explanation would make it seem more complicated. But now I know about the shorter representation, thanks!

Zarah answered 4/11, 2023 at 13:56 Comment(0)
C
0

Thanks for the reply! However, I already know how to set the bitmasks, I just don't know how to give individual tiles in a tileset their own collision layers. Is that even possible, or do I have to make the platforms their own nodes?

Constitutional answered 5/11, 2023 at 9:47 Comment(0)
C
0

I have finally found my answer! It lies in the physics materials of the tilemap.

First, I went to the Physics Layers menu in the tilemap inspector, added a new element and gave it its own collision layer:

Then I went to the tileset tab at the bottom of the screen, clicked on paint, selected "Physics Layer 1". clicked on my platform tile, and enabled one-way collision:

And voila! With the player script toggling the second bitmask, I can drop through as many platforms as I want while still not falling through my red tiles.

Constitutional answered 5/11, 2023 at 10:23 Comment(0)
C
0

Do I need to mark my question as solved?

Constitutional answered 5/11, 2023 at 10:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.