Steering is broken

Godot Version

4.7-rc2.mono

Question

So i created a kart racer, and it’s steering is NOT like arcade steering at all.

And i’m not a heavy professor math teacher math knowledge level so

could anyone fix it? Thank you!

(lfs) lfsuser:~$ extends VehicleBody3D
class_name BaseCar

@export var ARCADE_TURN_SPEED = 2.5
@export var STEER_SPEED = 3.0
@export var STEER_LIMIT = 0.6
var steer_target = 0
@export var engine_force_value = 40

@export var cam: Camera3D
@export var first: AudioStreamPlayer
@export var luigi: AudioStreamPlayer3D
@export var mario: AudioStreamPlayer3D
@export var yshi: AudioStreamPlayer
@export var race_done = false
@export var marker3d: Marker3D
@export var ui: Control
@export var boost: AudioStreamPlayer3D
@export var star: AudioStreamPlayer
@export var starparticle: GPUParticles3D
@export var marker3d2: Marker3D
@export var marker3d3: Marker3D
@export var camera: Camera3D

var gearshift = 3
var gear_multiplicator = 1
var gear_locked = false
var riding = false

var fwd_mps : float
var speed: float

# New steering variables
var turn_input = 0.0
var turn_speed = 4.0
var turn_stop_limit = 0.75
var steering_angle = 45.0

# Checkpoint system
var last_checkpoint: Area3D = null
var start_position: Vector3
var start_rotation: Vector3
var checkpoints_collected: Array = []
var total_checkpoints: int = 0
var lap_count: int = 0
var max_laps: int = 3
var golden_timer_active = false
var star_power = false

func _ready():
        start_position = global_position
        start_rotation = global_rotation
        add_to_group("kart")
        
        total_checkpoints = get_tree().get_nodes_in_group("checkpoints").size()
        
        if Global_self.GodotKartDoubleDash == true:
                riding = true

func _physics_process(delta):
        speed = linear_velocity.length() * Engine.get_frames_per_second() * delta
        fwd_mps = transform.basis.x.x
        
        process_gear_shift()
        
        if Global_self.GodotKartDoubleDash == true:
                process_items(delta)
                process_camera(delta)
        
        process_accel(delta)
        process_steer(delta)
        process_brake(delta)
        
        # Reset rotation if car flips over
        if abs(rotation.x) > deg_to_rad(150) or abs(rotation.z) > deg_to_rad(150) or abs(rotation.x) >= deg_to_rad(50) or abs(rotation.z) >= deg_to_rad(50):
                rotation.x = 0.0
                rotation.z = 0.0
        
        %Hud/speed.text = str(round(speed * 3.6)) + "  KMPH"
        %Hud/lap_label.text = "Lap: " + str(lap_count) + "/" + str(max_laps)

func process_accel(delta):
        if riding and not race_done and not Global_self.GodotKartDoubleDash:
                if Input.is_action_pressed("player_up"):
                        if fwd_mps >= -1:
                                if speed < 30 and speed != 0:
                                        engine_force = clamp(engine_force_value * 10 / speed, 0, 300)
                                else:
                                        engine_force = engine_force_value
                        engine_force = engine_force * gear_multiplicator
                        return
                if Input.is_action_pressed("player_dovn"):
                        if speed < 20 and speed != 0:
                                engine_force = -clamp(engine_force_value * 3 / speed, 0, 300)
                        else:
                                engine_force = -engine_force_value
                        return
                engine_force = 0
        
        if riding and not race_done and Global_self.GodotKartDoubleDash:
                if Input.is_action_pressed("player_a_btn"):
                        if fwd_mps >= -1:
                                if speed < 30 and speed != 0:
                                        if star_power:
                                                engine_force = clamp(engine_force_value * 10 / speed, 0, 1200)
                                        else:
                                                engine_force = clamp(engine_force_value * 10 / speed, 0, 500)
                                else:
                                        engine_force = engine_force_value
                        engine_force = engine_force * gear_multiplicator
                        return
                if Input.is_action_pressed("key_z"):
                        if speed < 20 and speed != 0:
                                if star_power:
                                        engine_force = -clamp(engine_force_value * 10 / speed, 0, 1200)
                                else:
                                        engine_force = -clamp(engine_force_value * 10 / speed, 0, 500)
                        else:
                                engine_force = -engine_force_value
                        return
                engine_force = 0

func process_steer(delta):
        if not riding:
                return
        
        # get smooth input
        turn_input = Input.get_axis("player_right", "player_left") * deg_to_rad(steering_angle)
        
        # only steer if moving fast enough
        if linear_velocity.length() > turn_stop_limit:
                # rotate the entire kart smoothly
                var new_basis = global_transform.basis.rotated(global_transform.basis.y, turn_input) 
                global_transform.basis = global_transform.basis.slerp(new_basis, turn_speed * delta) 
                global_transform = global_transform.orthonormalized()
        
        # wheel visuals only
        steering = turn_input * 0.3

func process_brake(delta):
        if riding and not race_done:
                if Input.is_action_pressed("player_crouch"):
                        brake = 1.0
                        $wheal3.wheel_friction_slip = 2
                        $wheal2.wheel_friction_slip = 2
                else:
                        brake = 0.0
                        $wheal3.wheel_friction_slip = 2.9
                        $wheal2.wheel_friction_slip = 2.9

func collect_itembox():
        ui.itemcontrollerui._on_rolling_single()

func collect_itemboxdouble():
        ui.itemcontrollerui._on_rolling_double()

func process_camera(delta):
        if Global_self.GodotKartDoubleDash:
                if Input.is_action_pressed("player_attack"):
                        camera.position = marker3d2.position
                        camera.rotation = marker3d2.rotation
                if Input.is_action_just_released("player_attack"):
                        camera.position = marker3d3.position
                        camera.rotation = marker3d3.rotation

func process_items(delta):
        if Input.is_action_just_pressed("player_crouch"):
                if golden_timer_active:
                        shroom_boost(delta)
                        return
                
                if ui.itemcontrollerui.item == "empty" and ui.itemcontrollerui.item2 == "empty":
                        return
                
                if ui.itemcontrollerui.item != "empty":
                        if ui.itemcontrollerui.item == "golden":
                                activate_golden(delta)
                        elif ui.itemcontrollerui.item == "shroom":
                                shroom_boost(delta)
                                ui.itemcontrollerui.item = "empty"
                                ui.itemcontrollerui.clearitem()
                        elif ui.itemcontrollerui.item == "star":
                                use_star(delta)
                                ui.itemcontrollerui.item = "empty"
                                ui.itemcontrollerui.clearitem()
                                
                elif ui.itemcontrollerui.item2 != "empty":
                        if ui.itemcontrollerui.item2 == "golden":
                                activate_golden(delta)
                        elif ui.itemcontrollerui.item2 == "shroom":
                                shroom_boost(delta)
                                ui.itemcontrollerui.item2 = "empty"
                                ui.itemcontrollerui.clearitem2()
                        elif ui.itemcontrollerui.item2 == "star":
                                use_star(delta)
                                ui.itemcontrollerui.item2 = "empty"
                                ui.itemcontrollerui.clearitem2()

func use_golden(delta):
        shroom_boost(delta)

var star_infinite = false
var star_checking = false

func use_star(delta):
        yshi.stop()
        apply_star_color()
        boost.play()
        starparticle.emitting = true
        star.play()
        engine_force_value = 760 * 2
        star_power = true
        star_infinite = false
        star_checking = true
        
        for i in range(50000):
                if Input.is_action_just_pressed("dpad_dovn"):
                        star_infinite = true
                        print("CHAOS! Infinite star activated!")
                        break
                await get_tree().create_timer(0.01).timeout
        
        star_checking = false
        
        if not star_infinite:
                await get_tree().create_timer(20.0).timeout
                _deactivate_star()

func _deactivate_star():
        star_power = false
        star_infinite = false
        star_checking = false
        yshi.play()
        remove_star_color()
        starparticle.emitting = false
        star.stop()
        engine_force_value = 40

func apply_star_color():
        var star_shader = ShaderMaterial.new()
        star_shader.shader = preload("res://stuff/godotKart/godotKartDoubleDash/shaders/star_effect.gdshader")
        apply_star_recursive(self, star_shader)

func apply_star_recursive(node, star_mat):
        for child in node.get_children():
                if child is MeshInstance3D:
                        child.material_overlay = star_mat
                apply_star_recursive(child, star_mat)

func remove_star_color():
        remove_star_recursive(self)

func remove_star_recursive(node):
        for child in node.get_children():
                if child is MeshInstance3D:
                        child.material_overlay = null
                remove_star_recursive(child)

func activate_golden(delta):
        golden_timer_active = true
        var timer = get_tree().create_timer(5.0)
        timer.timeout.connect(_on_golden_timer_timeout)

func _on_golden_timer_timeout():
        golden_timer_active = false
        print("golden ran out!")
        if ui.itemcontrollerui.item == "golden":
                ui.itemcontrollerui.item = "empty"

func use_shroom(delta):
        shroom_boost(delta)
        ui.itemcontrollerui.item = "empty"

func shroom_boost(delta):
        linear_velocity += transform.basis.x * 20.0
        boost.play()

func process_gear_shift():
        if riding and not race_done:
                if Input.is_action_pressed("gear"):
                        if gear_locked == false:
                                gear_locked = true
                                gearshift += 1
                                if gearshift == 6: gearshift = 1
                                if gearshift == 1: gear_multiplicator = 0.3
                                elif gearshift == 2: gear_multiplicator = 0.7
                                elif gearshift == 3: gear_multiplicator = 1
                                elif gearshift == 4: gear_multiplicator = 1.3
                                elif gearshift == 5: gear_multiplicator = 1.8
                                await get_tree().create_timer(1.0).timeout
                                gear_locked = false

func collect_checkpoint(checkpoint: Area3D):
        if Global_self.GodotKartDoubleDash == true:
                last_checkpoint = checkpoint
                
                if not checkpoints_collected.has(checkpoint):
                        checkpoints_collected.append(checkpoint)
                
                print("Checkpoint collected: ", checkpoint.name, " (", checkpoints_collected.size(), "/", total_checkpoints, ")")
                
                if checkpoints_collected.size() >= total_checkpoints:
                        lap_complete()

func lap_complete():
        lap_count += 1
        print("LAP ", lap_count, "/", max_laps, " COMPLETE!")
        
        if lap_count >= max_laps:
                race_finished()
        else:
                checkpoints_collected.clear()
                
                for checkpoint in get_tree().get_nodes_in_group("checkpoints"):
                        if checkpoint.has_method("reset_checkpoint"):
                                checkpoint.reset_checkpoint()
                
                print("Checkpoints reset for lap ", lap_count + 1)

func race_finished():
        print("RACE FINISHED! Final position achieved!")
        race_done = true
        speed = 0.0
        riding = false
        $wheal2.use_as_steering = false
        $wheal3.use_as_steering = false
        fwd_mps = 0.0
        steer_target = 0.0
        cam.position = marker3d.position
        cam.rotation = marker3d.rotation
        yshi.stop()
        first.play()
        luigi.play()
        await get_tree().create_timer(1.71).timeout
        mario.play()

func respawn_to_checkpoint():
        if Global_self.GodotKartDoubleDash == true:
                linear_velocity = Vector3.ZERO
                angular_velocity = Vector3.ZERO
                steering = 0.0
                engine_force = 0.0
                brake = 0.0
                
                if last_checkpoint:
                        global_position = last_checkpoint.global_position + Vector3.UP * 1.0
                        global_rotation = last_checkpoint.global_rotation
                else:
                        global_position = start_position
                        global_rotation = start_rotation
                
                rotation.x = 0.0
                rotation.z = 0.0

func _input(event):
        if Global_self.GodotKartDoubleDash == true and riding and not race_done:
                if event.is_action_pressed("respawn"):
                        respawn_to_checkpoint()
        if star_infinite and star_power:
                if event.is_action_pressed("dpad_dovn"):
                        print("Infinite star deactivated!")
                        _deactivate_star()

We have not seen your game, nor read your mind. Assume we do not know what “arcade steering” is either. You will have to explain a lot more, what are you expecting to happen versus what really happens? What chunk of this large block of code do you suspect causes your issue?

If you want arcade physics you shouldn’t use a Vehiclebody3D

You can check Kenney’s racing starter kit for an arcade physics car:

so i used this code with this logic of steering:

also hope this is the correct code for kart collision of mkdd

```cpp

#include “Sato/ObjCollision.h”
#include “JSystem/JGeometry/Vec.h”
#include “Sato/stMath.h”
#include “mathHelper.h”

// UNUSED, size match, inlines into other constructors
void ObjColBase::initialize()
{
mPos.zero();
mBoundDepth = 0.0f;
}

// UNUSED, size match, needed for sdata ordering
ObjColBase::ObjColBase(J3DModelData *modelData, ObjColBase::CKind kind) : mKind(kind), mScale(1.0f)
{
if (!modelData) { return; }

initRadius(modelData);
initialize();

}

ObjColBase::ObjColBase(J3DModelData *modelData, JGeometry::TVec3f scale, CKind kind) : mKind(kind)
{
if (!modelData) { return; }

initRadius(modelData);
setScale(scale);
initialize();

}

// UNUSED, size match
ObjColBase::ObjColBase(J3DModelData *modelData, f32 scale, ObjColBase::CKind kind) : mKind(kind), mScale(scale)
{
if (!modelData) { return; }

initRadius(modelData);
initialize();

}

void ObjColBase::initRadius(J3DModelData *model)
{
f32 maxRadius = 0.0f;

int nJoints = 3;
if (nJoints >= model->mJointTree.mJointCnt) { nJoints = model->mJointTree.mJointCnt; }

for (u8 i = 0; i < nJoints; i++) {
    if (maxRadius < model->mJointTree.mJoints[i]->mRadius) { maxRadius = model->mJointTree.mJoints[i]->mRadius; }
}
mRadius = maxRadius;
mPos.zero();

}

ObjColBase::ObjColBase(f32 radius, f32 scale, CKind kind)
{
mRadius = radius;
mKind = kind;
mScale = scale;
mPos.zero();
mBoundDepth = 0.0f;
}

void ObjColBase::setScale(const JGeometry::TVec3f &scale)
{
f32 s = scale.x;
if (scale.y > s) { s = scale.y; }
if (scale.z > s) { s = scale.z; }
mScale = s;
}

u8 ObjColBase::IsHitBoundPos(JGeometry::TVec3f objPos, JGeometry::TVec3f boundPos)
{
JGeometry::TVec3f deltaPos;
deltaPos.sub(objPos, boundPos);

u8 isHit = 0;
f32 minSeparation = mRadius * mScale;
if (deltaPos.squared() < minSeparation * minSeparation) { isHit = 1; }
return isHit;

}

bool ObjColSphere::IsHitSphere(JGeometry::TVec3f posOther, JGeometry::TVec3f posThis, f32 otherRadius)
{
bool isHit = false;
mPos.sub(posThis, posOther);
f32 minSeparationSq, distanceSq;
if (checkRadialCollisionXYZ(otherRadius, mPos, minSeparationSq, distanceSq)) {
f32 minSeparation = JMAFastSqrt(minSeparationSq);
f32 distance = JMAFastSqrt(distanceSq);
mBoundDepth = minSeparation - distance;
stVecNormalize(mPos);
isHit = true;
}
return isHit;
}

bool ObjColSphere::IsHitCylinder(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther,
const ObjColCylinder &cylinderCol)
{
bool isHit = false;
f32 sphereScaledRadius = mRadius * getScale();
f32 deltaX, deltaZ, minSeparationSq, distanceSq;
if (cylinderCol.checkRadialCollisionXZ(sphereScaledRadius, posThis, posOther,
minSeparationSq, distanceSq, deltaX, deltaZ)) {
if ((posThis.y - sphereScaledRadius < posOther.y + cylinderCol.getScaledHeight()) &&
(posThis.y + sphereScaledRadius > posOther.y)) {
f32 minSeparation = JMAFastSqrt(minSeparationSq);
f32 distance = JMAFastSqrt(distanceSq);
mBoundDepth = minSeparation - distance;
mPos.set(deltaX, 0.0f, deltaZ);
stVecNormalize(mPos);
isHit = true;
}
}
return isHit;
}

bool ObjColCylinder::IsHitSphere(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, f32 otherRadius)
{
bool isHit = false;
f32 deltaX, deltaZ, minSeparationSq, distanceSq;
if (checkRadialCollisionXZ(otherRadius, posThis, posOther, minSeparationSq, distanceSq,
deltaX, deltaZ) &&
posOther.y + otherRadius > posThis.y) {
if (posOther.y - otherRadius < (mCylinderHeight * mScale) + posThis.y) {
f32 minSeparation = JMAFastSqrt(minSeparationSq);
f32 distance = JMAFastSqrt(distanceSq);
mBoundDepth = minSeparation - distance;
mPos.set(deltaX, 0.0f, deltaZ);
stVecNormalize(mPos);
isHit = true;
}
}
return isHit;
}

bool ObjColCylinder::IsHitCylinder(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther,
const ObjColCylinder &colOther)
{
bool isHit = false;
f32 deltaX, deltaZ, minSeparationSq, distanceSq;
if (checkRadialCollisionXZ(colOther.mCylinderRadius * colOther.mScale, posThis, posOther,
minSeparationSq, distanceSq, deltaX, deltaZ)) {
if ((posThis.y < posOther.y + colOther.getScaledHeight()) &&
(posThis.y + (mCylinderHeight * mScale) > posOther.y)) {
f32 minSeparation = JMAFastSqrt(minSeparationSq);
f32 distance = JMAFastSqrt(distanceSq);
mBoundDepth = minSeparation - distance;
mPos.set(deltaX, 0.0f, deltaZ);
stVecNormalize(mPos);
isHit = true;
}
}
return isHit;
}

void ObjColCube::makeParameter(J3DModelData *modelData, Mtx matrix)
{
// bounding-box verts
J3DJoint *j = modelData->mJointTree.mJoints[0];
JGeometry::TVec3f min(j->mMin);
JGeometry::TVec3f max(j->mMax);

mVertices[0].set(min.x, min.y, max.z);
mVertices[1].set(min.x, min.y, min.z);
mVertices[2].set(max.x, min.y, min.z);
mVertices[3].set(max.x, min.y, max.z);
mVertices[4].set(min.x, max.y, max.z);

// transform verts
JGeometry::TVec3f t[5];
JGeometry::TVec3f *src = mVertices, *dst = t;
for (u8 i = 0; i < 5; i++, src++, dst++) {
    PSMTXMultVec(matrix, src, dst);
}

JGeometry::TVec3f edges[5];
edges[0].sub(t[1], t[0]);
edges[1].sub(t[2], t[1]);
edges[2].sub(t[3], t[2]);
edges[3].sub(t[0], t[3]);
edges[4].sub(t[4], t[0]);

// edge lengths
mDimensions[0] = edges[0].length();
mDimensions[1] = edges[1].length();
mDimensions[2] = edges[4].length();

const u8 topIndex = 4;

JGeometry::TVec3f normal, *edge = edges;
for (u8 i = 0; i < topIndex; i++, edge++) {
    normal.cross(edges[topIndex], *edge);
    stMakePlaneParam(mSidePlanes[i], normal, t[i]);
}

stMakePlaneParam(mTopPlane, edges[topIndex], t[topIndex]);

}

void ObjColCube::updateParameter(Mtx matrix)
{
// transform verts
JGeometry::TVec3f t[5];
JGeometry::TVec3f *src = mVertices, *dst = t;
for (u8 i = 0; i < 5; i++, src++, dst++) {
PSMTXMultVec(matrix, src, dst);
}

JGeometry::TVec3f normal;

JGeometry::TVec3f *vertex = t;
stPlaneParam *plane = mSidePlanes;
for (u8 i = 0; i < 4; i++, plane++, vertex++) {
    normal.set(plane->x, plane->y, plane->z);
    stMakePlaneParam(*plane, normal, *vertex);
}

normal.set(mTopPlane.x, mTopPlane.y, mTopPlane.z);
stMakePlaneParam(mTopPlane, normal, t[4]);

}

bool ObjColCube::chkIsHitQuad(const JGeometry::TVec3f &spherePos, const f32 &radius)
{
f32 penetrations[4];
for (u8 i = 0; i < 4; i++) {
f32 depth;
if (stCollideSurfaceAndSphere(spherePos, radius, mSidePlanes[i], depth) == 1) {
penetrations[i] = depth;
} else {
return false;
}
}
u8 closeFaces[4];
u8 farFaces[2];
u8 mDimIndices[2] = {1, 0};

if (penetrations[0] > penetrations[2]) {
    closeFaces[0] = 0;
    farFaces[0] = 2;
} else {
    closeFaces[0] = 2;
    farFaces[0] = 0;
}

if (penetrations[1] > penetrations[3]) {
    closeFaces[1] = 1;
    farFaces[1] = 3;
} else {
    closeFaces[1] = 3;
    farFaces[1] = 1;
}

for (u8 i = 0; i < 2; i++) {
    f32 adjusted = penetrations[closeFaces[i]] - (2.0f * radius);
    if (adjusted > mDimensions[mDimIndices[i]]) { return false; }
}

u8 farFaceIdx;
if (penetrations[farFaces[0]] > penetrations[farFaces[1]]) {
    farFaceIdx = 1;
} else {
    farFaceIdx = 0;
}

u8 minFaceIdx = farFaces[farFaceIdx];

stPlaneParam &plane = mSidePlanes[minFaceIdx];
mPos.set(plane.x, plane.y, plane.z);
mBoundDepth = penetrations[minFaceIdx];

return true;

}

bool ObjColCube::IsHitSphere(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, f32 radius)
{
JGeometry::TVec3f deltaPos;
deltaPos.sub(posOther, posThis);

f32 dotProduct = stDot(mTopPlane, deltaPos);

if ((dotProduct < 0.0f && -dotProduct > radius) || dotProduct > mDimensions[2] + radius) { return false; }

return chkIsHitQuad(posOther, radius);

}

bool ObjColCube::IsHitCylinder(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, const ObjColCylinder &cylinder)
{
f32 cylScale = cylinder.getScale();
f32 cylHeight = cylScale * cylinder.getCylinderHeight();
f32 thisY = posThis.y;
f32 cylY = posOther.y;

if ((thisY > cylY + cylHeight) || (thisY + mDimensions[2] < cylY)) { return false; }

return chkIsHitQuad(posOther, cylScale * cylinder.getCylinderRadius());

}

bool ExObjColBase::IsHitBound(JGeometry::TVec3f objPos, JGeometry::TVec3f boundPos)
{
bool isHit = false;
if (IsHitBoundPos(objPos, boundPos)) {
u8 nHits = 0;
if (boundPos.y < mBottom) { return 0; }
for (u8 i = 0; i < 4; i++) {
nHits += stSearchInSurface(boundPos, mSidePlanes[i]);
}
if (nHits == 4) { isHit = true; }
}
return isHit;
}

u8 ExObjColBase::IsHitBoundRadius(JGeometry::TVec3f pos, f32 radius)
{
u8 isHit = 0;
for (u8 i = 0; i < 4; i++) {
if (stCollideSurfaceAndSphere(pos, radius, mSidePlanes[i], mColDepth)) {
isHit = 1;
break;
}
}
return isHit;
}

void ExObjColBase::setBottom(f32 bottom)
{
mBottom = bottom - 100.0f;
}

// UNUSED, needed to generate assert string in .rodata
// educated guess at the exact code - size matches (0xd0)
void ExObjColBase::setWallParam(int index)
{
JUT_MINMAX_ASSERT(0, index, 4);

const JGeometry::TVec3f upVector(0.0f, 1.0f, 0.0f);

JGeometry::TVec3f normal;

normal.cross(upVector, mWallNormal);
normal.y = 0.0f;
stMakePlaneParam(mSidePlanes[index], normal, upVector);

}

void ObjColJump1::makeParameter(J3DModelData *modelData, Mtx matrix)
{
JGeometry::TVec3f vertices[6];
vertices[0].set(249.3f, 2.25f, -500.6f);
vertices[1].set(249.3f, 2.25f, 500.6f);
vertices[2].set(-249.3f, 2.25f, 500.6f);
vertices[3].set(-249.3f, 2.25f, -500.6f);
vertices[4].set(249.3f, 267.0f, 500.6f);
vertices[5].set(-249.3f, 267.0f, 500.6f);

// transform verts in-place
JGeometry::TVec3f *v = vertices;
for (u8 i = 0; i < 6; i++, v++) {
    PSMTXMultVec(matrix, v, v);
}

JGeometry::TVec3f edges[5];
edges[0].sub(vertices[1], vertices[0]);
edges[1].sub(vertices[2], vertices[1]);
edges[2].sub(vertices[3], vertices[2]);
edges[3].sub(vertices[0], vertices[3]);
edges[4].sub(vertices[5], vertices[3]);

const JGeometry::TVec3f upVector(0.0f, 1.0f, 0.0f);
JGeometry::TVec3f normal;

JGeometry::TVec3f *edge = edges;
for (u8 i = 0; i < 4; i++, edge++) {
    normal.cross(upVector, *edge);
    normal.y = 0.0f;
    stMakePlaneParam(mSidePlanes[i], normal, vertices[i]);
}

normal.cross(edges[4], edges[3]);
stMakePlaneParam(mTopPlane, normal, vertices[3]);

normal.cross(edges[1], edges[0]);
stMakePlaneParam(mBottomPlane, normal, vertices[1]);

mDirection.sub(vertices[4], vertices[1]);
mDirection.normalize();

}

f32 ObjColJump1::SearchWall(JGeometry::TVec3f pos1, JGeometry::TVec3f pos2)
{
mHitNum = 0;

u32 surfaceIndices[4] = {1, 4, 2, 0};

f32 t = stCollideLineToPlaneIn(pos1, pos2, mSidePlanes[0]);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 2; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices[i]]);
    }
    if (hitCount == 2) {
        mWallNormal.set(mSidePlanes[0].x, mSidePlanes[0].y, mSidePlanes[0].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        return t;
    }
}

t = stCollideLineToPlaneIn(pos1, pos2, mSidePlanes[2]);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 2; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices[i]]);
    }
    if (hitCount == 2) {
        mWallNormal.set(mSidePlanes[2].x, mSidePlanes[2].y, mSidePlanes[2].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        return t;
    }
}

surfaceIndices[0] = 0;
t = stCollideLineToPlaneIn(pos1, pos2, mSidePlanes[1]);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 3; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices[i]]);
    }
    if (hitCount == 3) {
        mWallNormal.set(mSidePlanes[1].x, mSidePlanes[1].y, mSidePlanes[1].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        return t;
    }
}

return -1.0f;

}

void ObjColJump1::Search(JGeometry::TVec3f pos1, JGeometry::TVec3f pos2)
{
f32 t = SearchWall(pos1, pos2);
if (mHitNum != 2) {
mNormal.set(mTopPlane.x, mTopPlane.y, mTopPlane.z);
stVecNormalize(mNormal);
mWallNormal.zero();

    f32 planeEval = mTopPlane.x * pos2.x + mTopPlane.z * pos2.z + mTopPlane.d;
    mHeight = -planeEval / mTopPlane.y;

    mHitNum = 1;
    mAttr = 6;
} else {
    mColDepth = stGetCollideDepthFromT(pos1, pos2, t);
    JGeometry::TVec3f delta;
    delta.sub(pos1, pos2);
}

}

bool ObjColJump1::IsHitSphere(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, f32 radius)
{
return false;
}

bool ObjColJump1::IsHitCylinder(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, const ObjColCylinder &cylinder)
{
return false;
}

// UNUSED, size 0x100
bool ObjColJump2::IsHitBound(JGeometry::TVec3f, JGeometry::TVec3f)
{
return false;
}

// UNUSED, size 0x64c
void ObjColJump2::makeParameter(J3DModelData *, Mtx)
{
// Need 2x12 0-bytes in .rodata somewhere in this area
f32 unused1[3] = {0.0f, 0.0f, 0.0f};
f32 unused2[3] = {0.0f, 0.0f, 0.0f};
}

// UNUSED, size 0x3b8
void ObjColJump2::Search(JGeometry::TVec3f, JGeometry::TVec3f) {}

// UNUSED, size 0x5c
void ObjColJump2::makeColParam(JGeometry::TVec3f, stPlaneParam, CrsGround::EAttr) {}

// UNUSED, size 0xac
u8 ObjColJump2::IsOnDashPlane(JGeometry::TVec3f)
{
return mIsDashPlane;
}

bool ObjColJump2::IsHitSphere(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, f32 radius)
{
return false;
}

bool ObjColJump2::IsHitCylinder(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, const ObjColCylinder &cylinder)
{
return false;
}

void ObjColBlock::makeParameter(J3DModelData *modelData, Mtx matrix)
{
// bounding-box verts
J3DJoint *joint = modelData->mJointTree.mJoints[0];
JGeometry::TVec3f min(joint->mMin);
JGeometry::TVec3f max(joint->mMax);

JGeometry::TVec3f vertices[5];
vertices[0].set(min.x, min.y, max.z);
vertices[1].set(min.x, min.y, min.z);
vertices[2].set(max.x, min.y, min.z);
vertices[3].set(max.x, min.y, max.z);
vertices[4].set(min.x, max.y, max.z);

// transform verts in-place
JGeometry::TVec3f *v = vertices;
for (u8 i = 0; i < 5; i++, v++) {
    PSMTXMultVec(matrix, v, v);
}

JGeometry::TVec3f edges[5];
edges[0].sub(vertices[1], vertices[0]);
edges[1].sub(vertices[2], vertices[1]);
edges[2].sub(vertices[3], vertices[2]);
edges[3].sub(vertices[0], vertices[3]);
edges[4].sub(vertices[4], vertices[0]);

const JGeometry::TVec3f upVector(0.0f, 1.0f, 0.0f);
JGeometry::TVec3f normal;
JGeometry::TVec3f *edge = edges;
for (u8 i = 0; i < 4; i++, edge++) {
    normal.cross(upVector, *edge);
    normal.y = 0.0f;
    stMakePlaneParam(mSidePlanes[i], normal, vertices[i]);
}

normal.cross(edges[1], edges[0]);
stMakePlaneParam(mTopPlane, normal, vertices[4]);

normal.cross(edges[0], edges[1]);
stMakePlaneParam(mBottomPlane, normal, vertices[1]);

f32 heightDiff = max.y - min.y;
f32 matrixY = matrix[1][3];
mBottom = matrixY - 0.5f * heightDiff;

mDirection.sub(vertices[4], vertices[0]);
mDirection.normalize();

}

f32 ObjColBlock::SearchWall(JGeometry::TVec3f pos1, JGeometry::TVec3f pos2)
{
mHitNum = 0;

u32 surfaceIndices1[4] = {0, 2, 4, 0};

f32 t = stCollideLineToPlaneIn(pos1, pos2, mSidePlanes[3]);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 3; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices1[i]]);
    }
    if (hitCount == 3) {
        mWallNormal.set(mSidePlanes[3].x, mSidePlanes[3].y, mSidePlanes[3].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        return t;
    }
}

t = stCollideLineToPlaneIn(pos1, pos2, mSidePlanes[1]);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 3; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices1[i]]);
    }
    if (hitCount == 3) {
        mWallNormal.set(mSidePlanes[1].x, mSidePlanes[1].y, mSidePlanes[1].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        return t;
    }
}

u32 surfaceIndices2[4] = {1, 3, 4, 0};

t = stCollideLineToPlaneIn(pos1, pos2, mSidePlanes[0]);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 3; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices2[i]]);
    }
    if (hitCount == 3) {
        mWallNormal.set(mSidePlanes[0].x, mSidePlanes[0].y, mSidePlanes[0].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        return t;
    }
}

t = stCollideLineToPlaneIn(pos1, pos2, mSidePlanes[2]);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 3; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices2[i]]);
    }
    if (hitCount == 3) {
        mWallNormal.set(mSidePlanes[2].x, mSidePlanes[2].y, mSidePlanes[2].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        return t;
    }
}

u32 surfaceIndices3[4] = {0, 1, 2, 3};

t = stCollideLineToPlaneIn(pos1, pos2, mBottomPlane);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 4; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices3[i]]);
    }
    if (hitCount == 4) {
        mWallNormal.set(mSidePlanes[2].x, mSidePlanes[2].y, mSidePlanes[2].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        mNormal.y *= -1.0f;
        return t;
    }
}

return -1.0f;

}

void ObjColBlock::Search(JGeometry::TVec3f pos1, JGeometry::TVec3f pos2)
{
f32 t = SearchWall(pos1, pos2);
if (mHitNum != 2) {
mNormal.set(mTopPlane.x, mTopPlane.y, mTopPlane.z);
stVecNormalize(mNormal);
mWallNormal.zero();

    f32 planeEval = mTopPlane.x * pos2.x + mTopPlane.z * pos2.z + mTopPlane.d;
    mHeight = -planeEval / mTopPlane.y;

    mHitNum = 1;
    mAttr = 1;
} else {
    mColDepth = stGetCollideDepthFromT(pos1, pos2, t);
}

}

bool ObjColBlock::IsHitSphere(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, f32 radius)
{
return false;
}

bool ObjColBlock::IsHitCylinder(JGeometry::TVec3f posThis, JGeometry::TVec3f posOther, const ObjColCylinder &cylinder)
{
return false;
}

// UNUSED, size 0x144
f32 ObjColBoard::SearchWall(JGeometry::TVec3f pos1, JGeometry::TVec3f pos2)
{
u32 surfaceIndices[4] = {0, 1, 2, 3};

f32 t = stCollideLineToPlaneIn(pos1, pos2, mBottomPlane);
if (0.0f <= t && 1.0f >= t) {
    u8 hitCount = 0;
    for (u8 i = 0; i < 4; i++) {
        hitCount += stSearchInSurface(pos2, mSidePlanes[surfaceIndices[i]]);
    }
    if (hitCount == 4) {
        mWallNormal.set(mSidePlanes[2].x, mSidePlanes[2].y, mSidePlanes[2].z);
        mHitNum = 2;
        mAttr = 2;
        mNormal = mDirection;
        mHeight = mBottom;
        mNormal.y *= -1.0f;
        return t;
    }
}

return -1.0f;

}

// UNUSED, size 0xf4
void ObjColBoard::Search(JGeometry::TVec3f pos1, JGeometry::TVec3f pos2) {}

#include “JSystem/JAudio/JASFakeMatch2.h”
```

Please format your code and only paste the part that does the ‘Steering’ thing. No one is going to read through the whole 1500+ line code.

I did, I used triple ticks but it didnt format it into code. Weird

Might be a bug or glitch.Press Ctrl + E in a new line and then paste the code inside the selected area.

It’s still way too much code to debug mentally, even with perfect formatting.

So the solution is back tire steering??? I do not know why this happens. Extremely weird…