Does anybody know how games like DOOM (1993) and Duke Nukem 3D achieve the U or half-eclipse shaped weapon bob when the player moves? I've been trying for ages using sinewaves but I just can't seem to find the winning equation.
The movement I'm talking about:

  • Toxe replied to this.
  • 09kingarthur I just made a quick test project. It uses two simple 2D curves for the horizontal and vertical sway and then offsets the weapon.

    weapon-bob.zip
    4kB

    extends Node2D
    
    @export var horizontal_sway: Curve
    @export var vertical_sway: Curve
    
    @onready var initial_position := position
    
    
    func _process(_delta: float) -> void:
        var t := wrapf(Time.get_unix_time_from_system(), 0.0, 1.0)
        position = initial_position + Vector2(horizontal_sway.sample(t) * 50.0, -vertical_sway.sample(t) * 50.0)

    09kingarthur Yeah that looks like (parts) of two sine waves.

    You could just use two curves in Godot, one for the x movement and one for y.

    09kingarthur I just made a quick test project. It uses two simple 2D curves for the horizontal and vertical sway and then offsets the weapon.

    weapon-bob.zip
    4kB

    extends Node2D
    
    @export var horizontal_sway: Curve
    @export var vertical_sway: Curve
    
    @onready var initial_position := position
    
    
    func _process(_delta: float) -> void:
        var t := wrapf(Time.get_unix_time_from_system(), 0.0, 1.0)
        position = initial_position + Vector2(horizontal_sway.sample(t) * 50.0, -vertical_sway.sample(t) * 50.0)

      Toxe Easier to do it without curves by just calling sin()

      • Toxe replied to this.

        xyz Yes and no. Curves are just nice because you can quickly change and customize them completely in a very visual way whereas changing a formula, well, it can be quicker if you know what you are doing math-wise or the formula is easy like a simple sine. But once it gets more complex curves are just neat because you actually see what you get.

        • xyz replied to this.

          Toxe In this case I doubt you'd want anything else other than pure sine so using a general purpose bezier curve is a bit excessive... and a bit slower.

          • Toxe replied to this.

            xyz Oh in this case you are right, yes. It's just that I wanted to explicitly use curves in this case because it makes it a bit easier to understand. Or maybe "self-documenting" would be the better word?

            Toxe Wait, you can make curves?! This is amazing! Using this method, I was able to recreate the weapon bob!
            What it looked like before:

            $cam/viewmodel.position.x += ((weapon_offset[equipped_weap].x)-(sin(weapon_bob*1)*0.03*(int(sprint)+1))-(($cam/viewmodel.position.x)))*0.1
            $cam/viewmodel.position.y += ((weapon_offset[equipped_weap].y)-(sin(weapon_bob*2)*0.015*(int(sprint)+1))-(($cam/viewmodel.position.y)))*0.1


            What it looks like now:

            $cam/viewmodel.position.x += ((weapon_offset[equipped_weap].x)-(weaponbob_x.sample(weapon_bob))*0.04*(int(sprint)+1)-(($cam/viewmodel.position.x)))*0.1
            $cam/viewmodel.position.y += ((weapon_offset[equipped_weap].y)+(weaponbob_y.sample(weapon_bob))*0.02*(int(sprint)+1)-(($cam/viewmodel.position.y)))*0.1


            Thank you so much for your help!!

              @09kingarthur @xyz For completeness here is the same but using sine. Although they are not exactly the same because the curves aren't real sine waves but Bézier curves.

              func _process(_delta: float) -> void:
                  var t := Time.get_unix_time_from_system()
                  var t_hori := wrapf(t, 0.0, 1.0)
                  var t_vert := wrapf(t + 0.25, 0.0, 1.0) * 2.0
              
                  var r := t_vert * PI
                  var v := 1.0 - sin(r if r < PI else r - PI)
                  var h := sin(t_hori * 2.0 * PI)
              
                  position = initial_position + Vector2(h * 50.0, -v * 50.0)

              Curves

              Sine

              Megalomaniak I may add an option for figure 8 or half-circle weapon bob later, for now I'm gonna keep the half-circle because I like the way it feels when you move around.

                Megalomaniak Absolutely! I think some of it also comes down to fine-tuning how the viewbob and weaponbob actually sync and are timed in general; I did a bit of tweaking to both and I think this iteration has the best feel so far.

                The gif's framerate doesn't really do it justice but it feels amazing when you're playing it.

                Looks pretty good.