I'm trying to implement a leaning mechanic in a game that I'm building. To do that I want to set one variable to act as the default number of rotation degrees (ideally x0, y0, and z0), and one for the rotation degrees of a character that is leaning to the right (ideally x0.6, y0, and z0).
Here's my code (for context, this script is attached to a Spatial node called UpperBody):
extends Spatial
const LEAN_LERP = 5
export var default_degrees : Vector3
export var leaning_degrees : Vector3
func _process(delta):
if Input.is_action_pressed("LeanRight"):
transform.origin = transform.origin.linear_interpolate(leaning_degrees, LEAN_LERP * delta)
else:
transform.origin = transform.origin.linear_interpolate(default_degrees, LEAN_LERP * delta)
if Input.is_action_pressed("LeanLeft"):
transform.origin = transform.origin.linear_interpolate(-leaning_degrees, LEAN_LERP * delta)
else:
transform.origin = transform.origin.linear_interpolate(default_degrees, LEAN_LERP * delta)
As you can see, I have both default_degrees and leaning_degrees' types set to Vector3 instead of the (currently unknown) equivalent for rotational degrees.
My question is this: how do I set a variable to contain rotational degrees?
Thanks.
There is no dedicated type for Euler angles. Instead you would use … drum roll … Vector3.
In fact, if you see the rotation_degrees property, you will find it is defined as a Vector3.
That, of course, isn't the only way to represent rotations/orientations. Ultimately, the Transform has two parts:
A Vector3 called origin which represents the translation.
A Basis called basis which represent the rest of the transformation (rotation, scaling and reflection, and shear or skewing).
A Basis can be thought of a trio of Vector3 each representing one of the axis of the coordinate system. Another way to think of Basis is as a 3 by 3 matrix.
Thus whatever you use to represent rotations or orientations will ultimately be converted to a Basis (and then either replace or be composed with the Basis of the transform).
Now, you want to interpolate the rotations, right? Euler angles aren't good for interpolation. Instead you could interpolate:
Transformations (Transform using interpolate_with Transform.interpolate_with).
Basis (Basis using Basis.slerp).
Quaternions (Quat using Quat.slerp).
On the other hand, Euler angles are good for input. In this particular case that means it is relative easy to wrap your head around what the numbers mean compared to writing any of these in the inspector.
Thus, we have two avenues:
Convert Euler angles to either Transform, Basis or Quat.
Find an easy way to input a Transform, Basis or Quat.
Euler angle to Quat
The Quat has a constructor that takes a vector for Euler angles. The catch is that it is Euler angles in radians. So we need to convert degrees to radians (which we can do with deg2rad). Like this:
var target_quat := Quat(
Vector3(
deg2rad(degrees.x),
deg2rad(degrees.y),
deg2rad(degrees.z)
)
)
Alternatively, you could do this:
var target_quat := Quat(degrees * PI / 180.0)
We also need to get the current quaternion from the transform:
var current_quat := transform.basis.get_rotation_quat()
Interpolate them:
var new_quat := current_quat.slerp(target_quat, LEAN_LERP * delta)
And replace the quat:
transform = Transform(
Basis(new_quat).scaled(transform.basis.get_scale()),
transform.origin
)
The above line assumes the transformation is only rotation, scaling, and translation. If we want to keep skewing, we can do this:
transform = Transform(
Basis(new_quat) * Basis(current_quat).inverse() * transform.basis,
transform.origin
)
The explanation for that is in the below section.
Notice we ended up converting the Quat to a Basis. So perhaps we are better off avoiding quaternions entirely.
Euler angle to Basis
The Basis class also has a constructor that works like the one we found in Quat. So we can do this:
var target_basis := Basis(degrees * PI / 180.0)
The catch this time is that Basis does not only represent rotation. So if we do that, we are losing scaling (and any other transformation the Basis has). We can preserve the scaling like this:
target_basis = target_basis.scaled(transform.basis.get_scale())
Ah, of course, the current Basis is this:
var current_basis := transform.basis
We interpolate like this:
var new_basis := current_basis.slerp(target_basis, LEAN_LERP * delta)
And we replace the Basis like this:
transform.basis = new_basis
To be honest, I'm not happy with the above approach. I'll show you a way to have the Basis you interpolate be only for rotation (so it can preserve any skewing the original Basis had, not only its scale), but it is a little more involved. Let us start here again:
var target_rotation := Basis(degrees * PI / 180.0)
And we will not scale that, instead we want to get a Basis that is only the rotation of the current one. We can do that by going from Basis to Quat and back:
var current_rotation := Basis(transform.basis.get_rotation_quat())
We interpolate the same way as before:
var new_rotation := current_rotation.slerp(target_rotation, LEAN_LERP * delta)
But to replace the Basis we want to keep everything about the old Basis that wasn't the rotation. In other words we are going to:
Take the Basis:
transform.basis
Remove its rotation (i.e. compose it with the inverse of its rotation):
Basis(transform.basis.get_rotation_quat()).inverse() * transform.basis
Which is the same as:
current_rotation.inverse() * transform.basis
And apply the new rotation:
new_rotation * current_rotation.inverse() * transform.basis
And that is what we set:
transform.basis = new_rotation * current_rotation.inverse() * transform.basis
I have tested to make sure the composition order is correct. And, yes, code for preserving skewing with Quat I showed above is based on this.
Euler angle to Transform
The way to create a Transform from Euler angles is via a Basis:
var target_transform := Transform(Basis(degrees * PI / 180.0), Vector3.ZERO)
We could preserve scale and translation with this approach:
var target_transform := Transform(
Basis(degrees * PI / 180.0).scaled(trasnform.basis.get_scale()),
transform.origin
)
If you want to interpolate translation at the same time, you can set your target position instead of transform.origin.
The current transform is, of course:
var current_transform := transform
We interpolate them like this:
var new_transform = current_transform.interpolate_with(target_transform, LEAN_LERP * delta)
And we can set that:
transform = new_trasnform
If we inline these variables, we have this:
transform = transform.interpolated_with(target_transform, LEAN_LERP * delta)
If you want to preserve skewing, use the Basis approach.
Alternative input to Euler angles
We have found out that interpolating transforms is actually very easy. Is there a way to easily input a Transform? Rhetorical question. We can add some Position3D to the scene. Position and rotate them (and even scale them, even though Position3D has no size), and then use the Transform from them.
We can make the Position3D children of your Spatial (which is somewhat odd, but don't think too hard about it), or as sibling. Regardless, the idea is that we are going to take the transform from these Position3D and use it to interpolate the transform of your Spatial. It is the same code as before:
transform = transform.interpolated_with(position.transform, LEAN_LERP * delta)
In fact, while we are at it, why not have three Position3D:
The lean left target.
The lean right target.
The default target.
Then you pick which target to use depending on input, and interpolate to that:
extends Spatial
const LEAN_LERP = 5
onready var left_target:Position3D = get_node(…)
onready var right_target:Position3D = get_node(…)
onready var default_target:Position3D = get_node(…)
func _process(delta):
var left := Input.is_action_pressed("LeanLeft")
var right := Input.is_action_pressed("LeanRight")
var target := default_target
if left and not right:
target = left_target
if right and not left:
target = right_target
transform = transform.interpolate_with(target, LEAN_LERP * delta)
Put the node paths where I left ....
Ok, Ok, here is one of the Euler angles versions:
extends Spatial
const LEAN_LERP = 5
export var default_degrees : Vector3
export var leaning_degrees : Vector3
func _process(delta):
var left := Input.is_action_pressed("LeanLeft")
var right := Input.is_action_pressed("LeanRight")
var degrees := default_degrees
if left and not right:
degrees = -leaning_degrees
if right and not left:
degrees = leaning_degrees
var target_rotation := Basis(degrees * PI / 180.0)
var current_rotation := Basis(transform.basis.get_rotation_quat())
var new_rotation := current_rotation.slerp(target_rotation, LEAN_LERP * delta)
transform.basis = new_rotation * current_rotation.inverse() * transform.basis
Related
I am making a space game in Godot and whenever my ship is a big distance away from (0,0,0) every time I move the camera or the ship, it shakes violently. Here is my code for moving the ship:
extends KinematicBody
export var default_speed = 500000
export var max_speed = 5000
export var acceleration = 100
export var pitch_speed = 1.5
export var roll_speed = 1.9
export var yaw_speed = 1.25
export var input_response = 8.0
var velocity = Vector3.ZERO
var forward_speed = 0
var vert_speed = 0
var pitch_input = 0
var roll_input = 0
var yaw_input = 0
var alt_input = 0
var system = "System1"
func _ready():
look_at(get_parent().get_node("Star").translation, Vector3.UP)
func get_input(delta):
if Input.is_action_pressed("boost"):
max_speed = 299792458
acceleration = 100
else:
max_speed = default_speed
acceleration = 100
if Input.is_action_pressed("throttle_up"):
forward_speed = lerp(forward_speed, max_speed, acceleration * delta)
if Input.is_action_pressed("throttle_down"):
forward_speed = lerp(forward_speed, 0, acceleration * delta)
pitch_input = lerp(pitch_input, Input.get_action_strength("pitch_up") - Input.get_action_strength("pitch_down"), input_response * delta)
roll_input = lerp(roll_input, Input.get_action_strength("roll_left") - Input.get_action_strength("roll_right"), input_response * delta)
yaw_input = lerp(yaw_input, Input.get_action_strength("yaw_left") - Input.get_action_strength("yaw_right"), input_response * delta)
func _physics_process(delta):
get_input(delta)
transform.basis = transform.basis.rotated(transform.basis.z, roll_input * roll_speed * delta)
transform.basis = transform.basis.rotated(transform.basis.x, pitch_input * pitch_speed * delta)
transform.basis = transform.basis.rotated(transform.basis.y, yaw_input * yaw_speed * delta)
transform.basis = transform.basis.orthonormalized()
velocity = -transform.basis.z * forward_speed * delta
move_and_collide(velocity * delta)
func _on_System1_area_entered(area):
print(area, area.name)
system = "E"
func _on_System2_area_entered(area):
print(area, area.name)
system = "System1"
Is there any way to prevent this from happening?
First of all, I want to point out, that this is not a problem unique to Godot. Although other engines have automatic fixes for it.
This happens because the precision of floating point numbers decreases as it goes away form the origin. In other words, the gap between one floating number and the next becomes wider.
The issue is covered in more detail over the game development sister site:
Why loss of floating point precision makes rendered objects vibrate?
Why does the resolution of floating point numbers decrease further from an origin?
What's the largest "relative" level I can make using float?
Why would a bigger game move the gameworld around the Player instead of just moving a player within a gameworld?
Moving player inside of moving spaceship?
Spatial Jitter problem in large unity project
Godot uses single precision. Support for double precision has been added in Godot 4, but that just reduces the problem, it does not eliminate it.
The general solution is to warp everything, in such way that the player is near the origin again. So, let us do that.
We will need a reference to the node we want to keep near the origin. I'm going to assume which node it is does not change during game play.
export var target_node_path:NodePath
onready var _target_node:Spatial = get_node(target_node_path)
And we will need a reference to the world we need to move. I'm also assuming it does not change. Furthermore, I'm also assuming the node we want to keep near the origin is a child of it, directly or indirectly.
export var world_node_path:NodePath
onready var _world_node:Node = get_node(target_node_path)
And we need a maximum distance at which we perform the shift:
export var max_distance_from_origin:float = 10000.0
We will not move the world itself, but its children.
func _process() -> void:
var target_origin := _target_node.global_transform.origin
if target_origin.length() < max_distance_from_origin:
return
for child in _world_node.get_children():
var spatial := child as Spatial
if spatial != null:
spatial.global_translate(-target_origin)
Now, something I have not seen discussed is what happens with physics objects. The concern is that The physics server might be trying to move them in the old position (in practice this is only a problem with RigidBody), and it will overwrite what we did.
So, if that is a problem… We can handle physic objects with a teleport. For example:
func _process() -> void:
var target_origin := _target_node.global_transform.origin
if target_origin.length() < max_distance_from_origin:
return
for child in _world_node.get_children():
var spatial := child as Spatial
if spatial != null:
var body_transform := physics_body.global_transform
var new_transform := Transform(
body_transform.basis,
body_transform.origin - target_origin
)
spatial.global_transform = new_transform
var physics_body := spatial as PhysicsBody # Check for RigidBody instead?
if physics_body != null:
PhysicsServer.body_set_state(
physics_body.get_rid(),
PhysicsServer.BODY_STATE_TRANSFORM,
new_transform
)
But be aware that the above code does not consider any physics objects deeper in the scene tree.
I'm trying to create a "Block" input that summons a wall at the position the camera is looking at and have it face the camera's direction. It seems to be using the global coordinates which doesn't make sense to me, because I use the same code to spawn a bullet with no problem. Here's my code:
if Input.is_action_just_pressed("light_attack"):
var b = bullet.instance()
muzzle.add_child(b)
b.look_at(aimcast.get_collision_point(), Vector3.UP)
b.shoot = true
print(aimcast.get_collision_point())
if Input.is_action_just_pressed("block"):
var w = wall.instance()
w.look_at(aimcast.get_collision_point(),Vector3.UP)
muzzle.add_child(w)
w.summon = true
The light attack input is the code used to summon and position the bullet. muzzle is the spawn location (just a spatial node at the end of the gun) and aimcast is a raycast from the center of the camera. All of this is run in a get_input() function. The wall spawns fine, I just can't orient it. I also need to prevent any rotation on the y-axis. This question is kinda hard to ask, so I couldn't google it. If you need any clarification please let me know.
New Answer
The asked comment made me realize there is a simpler way. In the old answer I was defining a xz_aim_transform, which could be done like this:
func xz_aim_transform(pos:Vector3, target:Vector3) -> Transform:
var alt_target := Vector3(target.x, pos.y, target.z)
return Transform.IDENTITY.translated(pos).looking_at(alt_target, Vector3.UP)
That is: make a fake target at the same y value, so that the rotation is always on the same plane.
It accomplishes the same thing as the approach in the old answer. However, it is shorter and easier to grasp. Regardless, I generalized the approach in the old answer, and the explanation still has value, so I'm keeping it.
Old Answer
If I understand correctly, you want something like look_at except it only works on the xz plane.
Before we do that, let us establish that look_at is equivalent to this:
func my_look_at(target:Vector3, up:Vector3):
global_transform = global_transform.looking_at(target, up)
The take away of that is that it sets the global_transform. We don't need to delve any deeper in how look_at works. Instead, let us work on our new version.
We know that we want the xz plane. Sticking to that will make it simpler. And it also means we don't need/it makes no sense to keep the up vector. So, let us get rid of that.
func my_look_at(target:Vector3):
# global_transform = ?
pass
The plan is to create a new global transform, except it is rotated by the correct angle around the y axis. We will figure out the rotation later. For now, let us focus on the angle.
Figuring out the angle will be easy in 2D. Let us build some Vector2:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle:float # = ?
# global_transform = ?
That part would not have been as easy with an arbitrary up vector.
Notice that we are using the 2D y for the 3D z values.
Now, we compute the angle:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2(0.0, -1.0))
# global_transform = ?
Since we are using the 2D y for the 3D z values, Vector2(0.0, -1.0) (which is the same as Vector2.UP, by the way) is representing Vector3(0.0, 0.0, -1.0) (Which is Vector3.FORWARD). So, we are computing the angle to the 3D forward vector, on the xz plane.
Now, to create the new global transform, we will first create a new basis from that rotation, and use it to create the transform:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2.UP)
var basis := Basis(Vector3.UP, angle)
global_transform = Transform(basis, position)
You might wonder why we don't use global_transform.rotated, the reason is that using that multiple times would accumulate the rotation. It might be ok if you only call this once per object, but I rather do it right.
There is one caveat to the method above. We are losing any scaling. This is how we fix that:
func my_look_at(target:Vector3):
var position := global_transform.origin
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2.UP)
var basis := Basis(Vector3.UP, angle).scaled(global_transform.basis.get_scale())
global_transform = Transform(basis, position)
And there you go. That is a custom "look at" function that works on the xz plane.
Oh, and yes, as you have seen, your code works with global coordinates. In fact, get_collision_point is in global coordinates.
Thus, I advice not adding your projectiles as children. Remember that when the parent moves, the children move with it, because they are placed relative to it.
Instead give them the same global_transform, and then add them to the scene tree. If you add them to the scene before giving them their position, they might trigger a collision.
You could, for example, add them directly as children to the root (or have a node dedicated to holding projectiles, another common option is to add them to owner).
That way you are doing everything on global coordinates, and there should be no trouble.
Well, since you are going to set the global_transform anyway, how about this:
func xz_aim_transform(position:Vector3, target:Vector3) -> Transform:
var position_2D := Vector2(position.x, position.z)
var target_2D := Vector2(target.x, target.z)
var angle := (target_2D - position_2D).angle_to(Vector2.UP)
var basis := Basis(Vector3.UP, angle)
return Transform(basis, position)
Then you can do this:
var x = whatever.instance()
var position := muzzle.global_transform.origin
var target := aimcast.get_collision_point()
x.global_transform = xz_aim_transform(position, target)
get_tree().get_root().add_child(x)
x.something = true
print(target)
By the way, this would be the counterpart of xz_aim_transform not constrained to the xz plane:
func aim_transform(position:Vector3, target:Vector3, up:Vector3) -> Transform:
return Transform.IDENTITY.translated(position).looking_at(target, up)
It took me some ingenuity, but here is the version constrained to an arbitrary plane (kind of, as you can see it does not handle all cases):
func plane_aim_transform(position:Vector3, target:Vector3, normal:Vector3) -> Transform:
normal = normal.normalized()
var forward_on_plane := Vector3.FORWARD - Vector3.FORWARD.project(normal)
if forward_on_plane.length() == 0:
return Transform.IDENTITY
var position_on_plane := position - position.project(normal)
var target_on_plane := target - target.project(normal)
var v := forward_on_plane.normalized()
var u := v.rotated(normal, TAU/4.0)
var forward_2D := Vector2(0.0, forward_on_plane.length())
var position_2D := Vector2(position_on_plane.project(u).dot(u), position_on_plane.project(v).dot(v))
var target_2D := Vector2(target_on_plane.project(u).dot(u), target_on_plane.project(v).dot(v))
var angle := (target_2D - position_2D).angle_to(forward_2D)
var basis := Basis(normal, angle)
return Transform(basis, position)
Here w - w.project(normal) gives you a vector perpendicular to the normal. And w.project(u).dot(u) gives you how many times u fit in w, signed. So we use that build our 2D vectors.
I am currently working on a game in Godot which involves rendering countries on a globe. I have very little prior experience with Godot, but have experimented with it in the past.
I am using this data from Natural Earth for country borders, and have successfully gotten it to display on the globe using a line mesh. The data was originally in shapefile format, but I converted it to GeoJSON using mapshaper.org.
Picture
The data basically boils down to a list of points given in latitude and longitude, which I then converted into 3d points and created a mesh using SurfaceTool.
I am having trouble generating an actual surface for the mesh, however. Firstly, I am unable to find a built-in function to generate a triangle mesh from this data. I have looked into numerous solutions, including using the built-in Mesh.PRIMITIVE_TRIANGLE_FAN format, which doesn't work on concave shapes.
I have looked into triangulation algorithms such as delaunay triangulation, but have had little success implementing them.
My current plan is to generate a triangle mesh using the 2d data (x,y = longitude,latitude), and project it onto the surface of the sphere. In order to produce a curved surface, I will include the vertices of the sphere itself in the mesh (example).
I would like to know how to go about constructing a triangle mesh from this data. In essence, I need an algorithm that can do the following things:
Create a triangle mesh from a concave polygon (country border)
Connect the mesh to a series of points within this polygon
Allow for holes within the polygon (for lakes, etc.)
Here is an example of the result I am looking for.
Again, I am quite new to Godot and I am probably over-complicating things. If there is an easier way to go about rendering countries on a globe, please let me know.
This is my current code:
extends Node
export var radius = 1
export var path = "res://data/countries.json"
func coords(uv):
return (uv - Vector2(0.5, 0.5)) * 360
func uv(coords):
return (coords / 360) + Vector2(0.5, 0.5)
func sphere(coords, r):
var angles = coords / 180 * 3.14159
return Vector3(
r * cos(angles.y) * sin(angles.x),
r * sin(angles.y),
r * cos(angles.y) * cos(angles.x)
)
func generate_mesh(c):
var mesh = MeshInstance.new()
var material = SpatialMaterial.new()
material.albedo_color = Color(randf(), randf(), randf(), 1.0)
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_LINE_STRIP)
for h in c:
var k = sphere(h, radius)
st.add_normal(k / radius)
st.add_uv(uv(h))
st.add_vertex(k)
st.index()
mesh.mesh = st.commit()
mesh.set_material_override(material)
return mesh
func load_data():
var file = File.new()
file.open(path, file.READ)
var data = JSON.parse(file.get_as_text()).result
file.close()
for feature in data.features:
var geometry = feature.geometry
var properties = feature.properties
if geometry.type == "Polygon":
for body in geometry.coordinates:
var coordinates = []
for coordinate in body:
coordinates.append(Vector2(coordinate[0], coordinate[1]))
add_child(generate_mesh(coordinates))
if geometry.type == "MultiPolygon":
for polygon in geometry.coordinates:
for body in polygon:
var coordinates = []
for coordinate in body:
coordinates.append(Vector2(coordinate[0], coordinate[1]))
add_child(generate_mesh(coordinates))
func _ready():
load_data()
What about using Geometry.triangulate_polygon() method to triangulate a polygon:
So I'm jumping into a physics engine. The colliders for now are spheres and planes. I've found the depth of the collision and the normal at point of contact easy enough, but for the life of me I can not wrap my head around the distribution of energy.
The Bodies contain a Collider, a Mass, a Force vector (velocity * mass), an Elasticity value (0 no bounce, 1 complete bounce) and a Friction value (0 slippery sausage, 1 momentum vampire) I've googled to hell and back and everything comes up with 1D and 2D simplifications, but I've been simply unable to adapt these to 3D.
Edit: I tried following this page:
http://www.plasmaphysics.org.uk/collision3d.htm. It seemed so simple but for some reason I still have no bounce with an elasticity of 1.
My implementation is below:
var v = new vec3(
(body.force.x + other.force.x) / totalMass,
(body.force.y + other.force.y) / totalMass,
(body.force.z + other.force.z) / totalMass
);
body.force.set(
((velA.x - v.x) * elasticity + v.x) * body.mass,
((velA.y - v.y) * elasticity + v.y) * body.mass,
((velA.z - v.z) * elasticity + v.z) * body.mass
);
other.force.set(
((velB.x - v.x) * elasticity + v.x) * other.mass,
((velB.y - v.y) * elasticity + v.y) * other.mass,
((velB.z - v.z) * elasticity + v.z) * other.mass
);
For elasticity I have tried both multiplying the elasticity of both bodies and getting the average of them; no change.
So a nights sleep, a bit of thinking and great help taken from the N+ physics explanation page I've hashed something together which works, though not necessarily physically accurate, for the sake of debugging it is split between many variables, but I have commented to the best of my ability.
//we have detected a collision between A) body B) other
//note: for the sake of making things easier to think about,
// anything moving into or along the collision normal is
// referred to as vertical, anything perpendicular to
// the collision normal, is referred to as horizontal.
//minimum translation required to resolve the collision
var mta = collision.normal.clone().multiplyScalar(collision.length);
//the total mass involved in the collision
var totalMass = body.mass + other.mass;
//the ratio of the mass belonging to body
var ratioA = body.mass / totalMass;
//the ratio of the mass belonging to other
var ratioB = other.mass / totalMass;
//the average elasticity of the collision
var elasticity = (body.elasticity + other.elasticity) / 2.0;
//the friction of the collision
//note: average works, but low values have strong effects,
// it is easier to work with if they are multiplied
var friction = body.friction * other.friction;
//the vertical force of body
var vertA = -body.force.clone().normalize().dot(collision.normal);
//the horizontal force of body
var horrA = 1.0 - Math.abs(vertA);
//the vertical force of other
var vertB = -other.force.clone().normalize().dot(collision.normal);
//the horizontal force of other
var horrB = 1.0 - Math.abs(vertB);
//the amount of force applied on body
var forceA = body.force.length();
//the amount of force applied on other
var forceB = other.force.length();
//the amount of vertical force applied on body
var vForceA = forceA * vertA;
//the amount of vertical force applied on other
var vForceB = forceB * vertB;
//the amount of horizontal force applied on body
var hForceA = forceA * horrA;
//the amount of horizontal force applied on other
var hForceB = forceB * horrB;
//the total vertical force of the collision
var verticalForce = (vForceA + vForceB) * elasticity;
//remove all vertical force from body
//resulting in a horizontal force vector
body.force.add(collision.normal.clone().multiplyScalar(forceA * vertA));
//apply friction to the horizontal force vector of body
body.force.add(body.force.clone().normalize().multiplyScalar(-friction * hForceA * body.imass));
//apply the new vertical force to body
body.force.add(collision.normal.clone().multiplyScalar(verticalForce * ratioA));
//remove all vertical force from other
//resulting in a horizontal force vector
other.force.add(collision.normal.clone().multiplyScalar(-forceB * vertB));
//apply friction to the horizontal force vector of other
other.force.add(other.force.clone().normalize().multiplyScalar(-friction * hForceB * other.imass));
//apply the new vertical force to other
other.force.add(collision.normal.clone().multiplyScalar(-verticalForce * ratioB));
//resolve collision taking into consideration mass
body.transform.position.sub(mta.clone().multiplyScalar(ratioA));
other.transform.position.add(mta.clone().multiplyScalar(ratioB));
I have a Rectangle on my Windows Phone page. When the user tilts their phone, the position of the Rectangle is changed (I use a TranslateTransform) based on the tilt. It works fine.
Like this:
void CurrentValueChanged(object sender,
SensorReadingEventArgs<AccelerometerReading> e)
{
// respond to the accelerometer on the UI thread
Dispatcher.BeginInvoke(new Action(() =>
{
// left, right
var _LeftDelta = e.SensorReading.Acceleration.X * 5d;
var _NewLeft = m_Transform.X + _LeftDelta;
var _PanelWidth = ContentPanel.RenderSize.Width;
var _RectangleWidth = m_Rectangle.RenderSize.Width;
if (_NewLeft > 0 && _NewLeft < _PanelWidth - _RectangleWidth)
m_Transform.X = _NewLeft;
// up, down
var _RightDelta = e.SensorReading.Acceleration.Y * -5d;
var _NewTop = m_Transform.Y + _RightDelta;
var _PanelHeight = ContentPanel.RenderSize.Height;
var _RectangleHeight = m_Rectangle.RenderSize.Height;
if (_NewTop > 0 && _NewTop < _PanelHeight - _RectangleHeight)
m_Transform.Y = _NewTop;
}));
}
What I would like to do, though, is add a bounce when the user hits the side of the page.
Anyone know how?
Your current code does not have acceleration separated from velocity.
the velocity should be updated based on the acceleration, rather than updating locations based on the acceleration.
http://en.wikipedia.org/wiki/Acceleration
Your value 5d takes the place of the mass. it tells you how much is happening for a given force.
You need to keep variables for location
x,y
and velocity
v_x , v_y
then update the locations with
x <- x+ v_x* step_size
y <- y+ v_y* step_size
and update the velocity:
v_x <- v_x + acceletation_x* 5d * step_size
v_y <- v_y + acceletation_y* 5d * step_size
Now, a bounce is trivial. When you reach an upper or lower edge, just flip the sign of the velocity: v_y -> -v_y, and for hitting a side, v_x -> -v_x.
You can make the bounce-back slower than the original velocity by multiplying with a constant when bouncing, for example v_x -> -v_x*0.7
will make the bounce velocity 70% of the initial speed.
You may also require some friction or things will just bounce around forever. Either you learn and implement some real model for that or just use some dummy way to slightly reduce the velocity in each step. The simplest thing that could give something like this is, in each step:
v_x <- v_x * 0.95
v_y <- v_y * 0.95