+1 vote

I use RigitBody2D for bullets. I want bullets to have physics, that is, they ricocheted and repelled opponents. But at high speed, they fly through walls and enemies. I added RayCast2D and now I can know if the bullet will collide with the object, but I do not know the function in which you can handle collisions. Is it possible to make Godot handle collisions if RayCast2D sees an object? Perhaps there are other ways of implementation? But it is important that the bullet had physics.

I'm new to Godot, please give a detailed explanation. And if you can, articles where this topic is revealed.

Godot v2.1.4

in Engine by (18 points)

4 Answers

+2 votes
Best answer

As others suggest, you probably have an object that is also too fast to be visualized for more than a few blips on the screen. Such bullets are better fully as ray casts, then some visual effect of a trace.

But if it is something you want to go ahead with anyway, one thing you could try is looking at the time of arrival using a ray cast, and place the body inside the wall if it will go past it next frame. This would be a bit of a hack / work around.

If your linear velocity is constant it is a little easier, if it is expected to continue to grow, then you will probably want to add the next frame's worth of acceleration to it before checking things.

The code might look something like this, your mileage may vary depending on the sizes of your colliders and such:

# Godot 2.1.4
const ERROR_AMOUNT = 0.00001

func _integrate_forces(state):

    var delta = state.get_step()

    var lv = state.get_linear_velocity()
    var heading = lv.normalized()
    var future_distance = lv.length() # + your_acceleration

    var current_pos = get_pos() # or get_global_pos()
    var destination = current_pos + heading * future_distance

    var cast_result = state.get_space_state().intersect_ray(current_pos, destination, [self])

    if(not cast_result.empty()):

        var dist_to_wall = (current_pos - cast_result.position).length()

        # Check how many frames it can go at this rate, 
        # if it is less than 1, it will pass through.
        if(dist_to_wall / (future_distance * delta) < 1.0 + ERROR_AMOUNT):

            # Should put the object inside the wall a little, 
            # so it can be detected.
            state.set_transform(Matrix32(0, cast_result.position))

            do_bullet_impact_here()
            return
by (5,245 points)
selected by

Thank you so much for your code, it works as I wanted.

set_pos(get_pos()+get_linear_velocity().clamped(get_pos().distance_to(cast_result.position)-5))

Works more correctly than:

state.set_transform(Matrix32(0, cast_result.position)) 

In your version, collisions with the wall are not very correct. The bullet bounces in the same direction in which it collided. Also I want to note that this method works only if the bullet object has a CCD parameter set in Cast Ray.

Glad to hear you got it working.

Interesting note about the CCD parameter, thanks for sharing your results. X)

i regestired just to upvote

+3 votes

You don't need a RayCast2D for this. RigidBody2D has methods for dealing with the fast motion issue.

Use set_continuous_collision_detection_mode(). You have three options:

CCDMODEDISABLED = 0 — Disables continuous collision detection. This is the fastest way to detect body collisions, but can miss small, fast-moving objects.
CCDMODECASTRAY = 1 — Enables continuous collision detection by raycasting. It is faster than shapecasting, but less precise.
CCD
MODECASTSHAPE = 2 — Enables continuous collision detection by shapecasting. It is the slowest CCD method, and the most precise.

Mode 1 is probably going to work fine for you. Give it a try and see the docs for more details:
http://docs.godotengine.org/en/2.1/classes/class_rigidbody2d.html

by (21,313 points)

I did so, but bullets still fly through the walls, nothing has changed.
Image

Does the bullet perform as expected if the speed is lower? What about your code? How are you moving the bullet?

I give a bullet when creating an impulse.
var i = bullet.instance ()
i.setpos ()
i.apply
impulse ()

Yes, at low speeds, the bullet behaves correctly.

If possible, could you make an example?
I have Godot 3.0 and Godot 2.1.4.
I can compare your and my decision and find the error.

I was just playing around with it, and it seems to get the bullet to tunnel through the walls when using CCD (ray mode), I have to apply such a large impulse that the bullet's velocity becomes huge, and I can barely see it anyway, except as a "dotted" line.

In the past, when I've wanted fast/instant bullets I've found that ray-casting makes for a better solution, especially if you want really small bullets as well. Cast rays from collision point to collision point as desired and you can ricochet, etc. (Note this can be done very efficiently using the DirectPhysics2DState rather than RayCast2D nodes). Add a Line2D/particles/shader for a "tracer" style visual effect. Most shooters typically do this.

I'm afraid I don't have any other solutions to suggest for you. High-speed + small-size is exactly where most physics engines hit their limit.

Sorry, but since I'm new, I did not understand what was being said. What is DirectPhysics2DState, Line2D, Shader? It would be much easier for me to see the project. I already wrote a small crutch, which works for me by 50-70%:

rc - RayCast2D
This code is executed in bullet.

func _fixed_process(delta): 
if rc.is_colliding():
    var dot = rc.get_collision_point()
    var i = get_pos().distance_to(dot)
    if i < 34:
        set_pos(get_pos()+get_linear_velocity().clamped(i-5))

Of course, I would like to find a more precise solution.

There's a doc on it here:
http://docs.godotengine.org/en/2.1/learning/features/physics/ray-casting.html

Or the 3.0 version:
http://docs.godotengine.org/en/latest/tutorials/physics/ray-casting.html

As for the rest, Line2D/shader/particles are different approaches you could take for creating visuals. It all depends on what visual effect you're going for.

I read these articles, it seems that I have already read all the articles related to the clashes ... I do not understand yet what decision you proposed to me, but I'll try to carefully read the article again, I can understand what you meant.

As I understood from the article Physics2DDirectSpaceState.intersect_ray() only helps to detect a collision. I do the same with RayCast2D. Sorry, but I still could not understand what you were offering as a solution.

0 votes

Another suggestion I see often is to make your walls thicker. Thicker hitboxes on your walls give it more time to detect the collision.

by (143 points)

Yes, I've seen similar advice, but the design of the game does not allow to make the walls thicker. I've already read a lot about this, but I have not found a solution, they always write about CCD, but how it works is not clear. For the second day I have been looking for a solution, I almost gave up. Now I'm trying to write a crutch.

If this works in your game, a possible crutch might be using multiple larger hitboxes for the wall that are one way. I'm assuming your walls are thin. If you overlap larger hitboxes that only accept collisions from one side then you could make the one from the left be wider but not collide with things coming from the right and vice versa. Seems hackish but might work.

The fact that the design of the game itself is designed for the fact that the walls should be 2 pixels thick. But the bullet overcomes the distance of 34 pixels per physical frame and this is not the fastest bullet. I do not want crutches to be visible to the players.
Do it well or don't it.

What I was thinking of wouldn't be visible to the end user at all. I made a quick mock scene that doesn't do anything but has all the nodes defined the way I was trying to explain. You can get at it here:

https://github.com/bitwes/Godot30Scratch/tree/master/ThickerWallsDemo

You should be able to see how I created a box for the wall, then made two collision shapes and made them "one way". I rotated them so that the arrow lines up with the edge of the wall. This allows the wall to be thicker in each direction but not stop bullets early on the other side. It's double the hitboxes, but maybe that doesn't matter.

In this case, the bullet behaves strangely and does not even bite the wall. Maybe I did something wrong? I downloaded your example and added RigitBody2D, then I added CollisionPolygon2D to it and set the linear speed (500, 0). The body crashed without touching the wall and rolled upwards.

Well, I guess I shoulda tried to do what I was saying...cause I had it backwards. I updated the demo. It has two rigid bodies that hit the wall from each side now. Seems to work ok. I had the one way boxes reversed. Download the demo again and give it a shot.

Now the bullet flew through the wall, and then it was drawn to her from the other side. The speed at which the bullet moves in my project 2000, and its size is 3 pixels. The thickness of the wall itself is 2 pixels. Your bullet is very big and moves very slowly. When I made your bullet smaller and faster, it did as I described above.

I made the extents on the bullet on the left 1.5, which would make the hitbox 3px x 3px. The sprite is just there so I can see where the bullet is. I updated the speed to 2000 and it doesn't go through the wall (the node FastFromLeft).

Did you change the size of the hitboxes on the wall? That will for sure break it. You can see in the demo that both "bullets" stop at the wall.

You can make the boxes Area2D instead of on a rigid body. You can set the precedence for the collision to make sure that things collide with the wall before they collide with anything else. Then you destroy the bullet when it hits the wall. Yes, the bullets will technically go through the wall but they won't hit anything else.
*edit change right to left.

If I change the speed to 20 000, then the bullet still will not fly through the wall? I need a bullet to bounce off the wall, have a mass and behave like RigidBody2D, but did not pass through walls at very high speeds. Try changing the frame rate in the physics section to 1-2. You can see step by step how your bullet moves. At the collision, a bullet of the crowd bounce off the wall, how would she do in reality. First set the speed to 100, then try to increase it by 50-100, every try.

I'm out of ideas. You might have to find some special way of treating your bullets once they get too fast. I think you are going to run into the same problems with other objects once they get moving too quick.

I marked the correct answer to the solution that suited me. Using this code, I can get my bullets to recoill, even if their speed is more than 20,000. Thank you for taking your time. Look at the code above, maybe you will find something interesting for yourself.

+2 votes

I'm obviously super-late, but this might help out others in the future. I had a similar problem and, for what's probably the first time in my life, I came up with my own solution!

It shares a little with avencherus's answer, but I admit I don't fully understand his code, so I can't point out the differences, other than that this is almost painfully simple.

Godot version 3.1.1 stable official.


Note: this is for when you aren't using a RigidBody(2D) or even move_and_slide()/move_and_collide(), though who knows, you might be able to adapt it to fit those cases.


I use a vector, velocity, and add it to position after checking that it won't put the object inside another collider.

The idea is that you cast a ray the length, and in the direction, of the velocity vector. If it hits something, it means you'll be colliding in the next frame, so instead of position += velocity, you get_collision_point() and set position = that (accounting for the object's size, since position is in its center; this is crucial, because raycasts also usually start from the center, and if you fail to do this it won't work properly; if you've moved raycast origin, take that as the center).


Note 2: I've only tested this for gravity so far. This means I've only cast 1 ray, vertically. I'll update the answer once I've done more work on my character controller.


Here's the basic code. It's what I got so far. I avoided putting in a lot of variable definitions, as they're obvious and I wanted to it to be uncluttered.

extends KinematicBody2D

func _physics_process(delta):
    # adding gravity
    velocity.y += GRAVITY

    # adding input movement
    # ...

    # raycasting
    raycast.cast_to.y = height + velocity.y * delta

    if raycast.is_colliding():
        position = raycast.get_collision_point() - Vector2(0, height)
        velocity.y -= GRAVITY
    else:
        position += velocity * delta

height is $CollisionShape2D.shape.extents.y, set in _ready(). My sprite (and collision shape) is a square.
velocity.y -= GRAVITY is to stop velocity.y from increasing unceasingly, or else it will interfere with input movement later. Outright setting it to 0 messes things up, since our raycast depends on it.

For accuracy's sake, you'll need more than one ray, but the underlying code should be the same.

From my tests so far, this works when I've jacked GRAVITY all the way up to 18000 — normally, it's only 50 — but fails at 19000 and onward. Do keep in mind this is gravity, so it's acceleration, meaning it boosts velocity.y to ridiculous values in just milliseconds, as it increases by that amount every frame.
I don't know what breaks things at this point, but I probably won't investigate either, because I can't see any use-case scenarios for it; except for space sims, I guess, but those are way above my level.

by (18 points)

Was having the same problem and the same idea as you did (allthough I use an Area2D and use translate instead of directly editing position), maybe not as cleanly coded as you did :P but well done!

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.