I have a very basic third person controller:
func _physics_process(delta):
vel.x = 0
if Input.is_action_pressed("move_forward"):
vel.x += SPEED * delta
if Input.is_action_pressed("turn_left"):
rotate_y(deg2rad(ROTATION_INTENSITY))
move_and_collide(vel)
However on rotating and then moving forward, the player will move forward on the x axis instead of the direction the player is pointed. How do i fix this?
Thanks in advance!
Your KinematicBody is an Spatial, and as such it is positioned by a Transform.
You can get the Transform from the parent Spatial (or the root of the scene tree if there is no parent Spatial) from the transform property.
And you can get the Transform from root of the scene directly from the global_transform property.
The Transform has two parts:
A Vector3 called origin.
A Basis called basis.
Together they encode a coordinate system. In particular basis has three vectors that hold the direction and scale of each of the axis of the coordinate system. And origin has the position of the origin of the coordinate system.
Now, Godot's coordinate system in 3D is right handed with UP = Y and FORWARD = -Z. You can confirm this with the constants Vector3.UP and Vector3.FORWARD. Also notice this differs from Unity and Unreal which are left handed.
Ergo, if you want the direction of one of the axis, you can get it form the basis of the global transform. Like this:
var forward := -global_transform.basis.z
var backward := global_transform.basis.z
var left := -global_transform.basis.x
var right := global_transform.basis.x
var down := -global_transform.basis.y
var up := global_transform.basis.y
You could use those to build your velocity in global coordinates, for example:
velocity += global_transform.basis.x * SPEED
Alternatively, you can convert vectors from local to global space with to_global (and from global to local with to_local) so you can do this:
var forward := to_global(Vector3.FORWARD)
var backward:= to_global(Vector3.BACKWARD)
var left := to_global(Vector3.LEFT)
var right := to_global(Vector3.RIGHT)
var down := to_global(Vector3.DOWN)
var up := to_global(Vector3.UP)
This also means you may build your velocity in local coordinates, and convert it to global coordinates at the end:
move_and_slide(to_global(velocity))
And just to be clear, move_and_slide expects global coordinates.
I will also point out that it is possible to rotate a vector with rotated and project a vector onto another with project.
Related
I'm trying to recreate Stickwar's flash game, a game in which I'm a big fan of but got suck when I attempt to align my troops. Particularly, here is the in game views of it:
As you can see, the are many stickmans who align next to each other. A horizon line is consist of five stickmans. If there is another stickman, it will automatically aligns on the next line and so on.
These Stickmans, however, do not always function like that. The stickman with archer's class will always stay behind all of the troops, while the guy with spear are always if in the front of the troops as leader. If the leader was spawn but the first line is full, then the other troops will automatically move back, leaving the first line empty, which is the same with the second lines and so on.
I have no idea how to build this up in Godot. So if there are anybody who have some hints, please let me know. I really appreciate all the efforts. This doesn't seems like a good question to ask but I honestly can't find any Godot tutorials related to it, so I'm really sorry if this question is bother you. (also sorry for my bad grammars, too)
And don't expect to find tutorials for everything. How does people who make the tutorials figure out things in the first place? What you need is to break this down into smaller problems that are addressable.
I'll figure it out as I write.
One part is that you need to have units that can move to a particular location. Which could be to attack an enemy, or, yes, to their spot in the formation.
Another is that you need to define the possible spot where the units can stand in formation.
And the last is that you need code that tells the units to which spot they should go. This code would make sure to not send multiple units to the same spot, and to follow whatever rules for the formations you have.
Given the game, I'm going to use Area2D for the units.
Why?
I'm not using Sprite or something graphic directly because they must be clickable by the player.
I'm not using KinematicBody2D nor RigidBody2D because I don't see the collide with each other.
And I'm not using StaticBody2D because they need to move freely.
So Area2D it is.
Now, these Area2D will be given a target to go to. We can use a Vector2 for that:
extends Area2D
export var target:Vector2
Let us make sure that the target matches their position when they are ready (And I remind you that Vector2.ZERO is a valid position):
extends Area2D
export var target:Vector2
func _ready() -> void:
target = global_position
I'm working with global coordinates because I don't want to have to convert to local coordinates of the unit when I write the code that will set these positions.
I remind you that the local space of different nodes is different.
Now, we need it to move towards that target. So we write _physics_process. Let us start with direction and distance:
extends Area2D
export var target:Vector2
func _ready() -> void:
target = global_position
func _physics_process(delta:float) -> void:
var direction := global_position.direction_to(target)
var distance := global_position.distance_to(target)
For reference, I could have also done it like this:
extends Area2D
export var target:Vector2
func _ready() -> void:
target = global_position
func _physics_process(delta:float) -> void:
var displacement := target - global_position
var direction := displacement.normalized()
var distance := displacement.length()
And let us figure out how much we can move per physics frame. Which also means I need to define a speed:
extends Area2D
export var target:Vector2
export var speed:float = 100.0
func _ready() -> void:
target = global_position
func _physics_process(delta:float) -> void:
var direction := global_position.direction_to(target)
var distance := global_position.distance_to(target)
var max_distance := speed * delta
Ok, I'm setting the speed to 100.0 because - given that we are working in 2D - it will be in pixels per second. And if it is something too small you are not going to see it. However, tweak the value to your needs.
And for the distance we can move in the current physics frame I'm multiplying speed by delta. Remember that delta is the elapsed time since the last frame, and also remember that speed is distance divided by time. So when we multiply the speed by time, the time cancels out and we have distance.
Now, we don't want to overshoot the target, so we are going to clamp the distance:
extends Area2D
export var target:Vector2
export var speed:float = 100.0
func _ready() -> void:
target = global_position
func _physics_process(delta:float) -> void:
var direction := global_position.direction_to(target)
var distance := min(global_position.distance_to(target), speed * delta)
And we need to move, of course:
extends Area2D
export var target:Vector2
export var speed:float = 100.0
func _ready() -> void:
target = global_position
func _physics_process(delta:float) -> void:
var direction := global_position.direction_to(target)
var distance := min(global_position.distance_to(target), speed * delta)
global_position += direction * distance
Just for reference I'll write the code for KinematicBody2D using move_and_slide:
extends KinematicBody2D
export var target:Vector2
export var speed:float = 100.0
func _ready() -> void:
target = global_position
func _physics_process(delta:float) -> void:
var direction := global_position.direction_to(target)
var distance := global_position.distance_to(target)
var velocity = direction * min(distance / delta, speed)
move_and_slide(velocity)
Again, speed is distance divided by time. And velocity is speed with a direction. I hope this makes sense.
We would also need to trigger animations and flip the sprite (which would be a child node) if necessary.
Something like this:
$AnimatedSprite.flip_h = direction.x < 0
if distance.is_equal_approx(Vector2.ZERO):
$AnimatedSprite.play("idle")
else:
$AnimatedSprite.play("walk")
About defining the posible spots, we could use Position2D. Perhaps group them as necessary.
However, as I'll describe below, we can use Path2D.
And to assign the targets...
We are going to start from the front line and move backwards. We are going to need to have the units in buckets, and exhaust the buckets in order.
Ok, there are multiple kinds of units. You can make an scene for each kind of unit, they can all use the script for motion that I have above. You can then also go ahead an set different node groups to them, so we can get them with get_tree().get_nodes_in_group.
Now, we have different rules for how each group uses each line. If I understand correctly, the leader will be alone on its line, soldiers will be in groups of 5, and archers will be all in the same line.
Thus, it would be useful to define the lines as... lines. I would say Line2D but that is graphical and we don't want these visible. So, instead we will use Path2D, which also has some convenient methods to find equally spaced points over them.
For convenience, let us make the Path2D children of the node where we will be assigning targets. So we can get them like this:
var paths:Array = get_children()
We also need the list of groups, for example:
var groups = ["Leader", "Soldier", "Archer"]
And the number of them we can group… Let us make a class (I suggest to put this at the end of the file).
class Group:
var name:String
var max_count:float
func _init(name:String, max_count:float):
self.name = name
self.max_count = max_count
Now the list of groups:
var groups = [
Group.new("Leader", 1),
Group.new("Soldier", 5),
Group.new("Archer", INF)
]
And we will be working down these in order:
var paths:Array = get_children()
for item in groups:
var group := item as Group
var units_in_group := get_tree().get_nodes_in_group(group.name)
if units_in_group.empty():
continue
var path := paths.pop_back()
var count := min(units_in_group.size(), group.max_count)
# figure out the positions
Ok, now I have another issue. I have a range from 0 to 1 to divide by n elements. When I have one element I want to get the position at the middle (0.5), but if I say 1/n when n is 1 I get 1 not 0.5.
So, 1/n will be the separation between elements. But I need to offset the position by half that.
I think I got it (I'm writing here, not testing it):
var paths:Array = get_children()
for item in groups:
var group := item as Group
var units_in_group := get_tree().get_nodes_in_group(group.name)
if units_in_group.empty():
continue
var path := paths.pop_back() as Path2D
var count := min(units_in_group.size(), group.max_count)
var separation := 1.0/count
var offset := separation * 0.5
for index in count:
var position := path.interpolatef(index * separation + offset)
# assign the position
I must be getting the position in the local space of the path. Under that assumption, I'll convert them to global before passing them to the unit.
var paths:Array = get_children()
for item in groups:
var group := item as Group
var units_in_group := get_tree().get_nodes_in_group(group.name)
if units_in_group.empty():
continue
var path := paths.pop_back() as Path2D
var count := min(units_in_group.size(), group.max_count)
var separation := 1.0/count
var offset := separation * 0.5
for index in count:
var unit := units_in_group[index]
var position := path.to_global(
path.interpolatef(
index * separation + offset
)
)
unit.target = position
Ah, but wait, we need to continue assigning units from the same group on the next path…
Let us start by erasing from units_in_group the units we have already assigned a target to, so we don't repeat. Which also means we cannot get them by index. So I will use pop_back instead:
var paths:Array = get_children()
for item in groups:
var group := item as Group
var units_in_group := get_tree().get_nodes_in_group(group.name)
if units_in_group.empty():
continue
var path := paths.pop_back() as Path2D
var count := min(units_in_group.size(), group.max_count)
var separation := 1.0/count
var offset := separation * 0.5
for index in count:
var unit := units_in_group.pop_back()
var position := path.to_global(
path.interpolatef(
index * separation + offset
)
)
unit.target = position
And now we can loop until it is empty:
var paths:Array = get_children()
for item in groups:
var group := item as Group
var units_in_group := get_tree().get_nodes_in_group(group.name)
while not units_in_group.empty():
var path := paths.pop_back() as Path2D
var count := min(units_in_group.size(), group.max_count)
var separation := 1.0/count
var offset := separation * 0.5
for index in count:
var unit := units_in_group.pop_back()
var position := path.to_global(
path.interpolatef(
index * separation + offset
)
)
unit.target = position
And I think that should work.
Perhaps we could do better… In particular, I would rather this assigned the unit that is nearest to the position we want.
We can find the best unit like this:
var position := path.to_global(
path.interpolatef(
index * separation + offset
)
)
var best_distance := INF
var best_unit:Node2D = null
for unit in units_in_group:
var c_unit := unit as Node2D
var c_distance := c_unit.global_position.distance_to(position)
if c_distance < best_distance:
best_distance = c_distance
best_unit = c_unit
Here c_ stands for current. I would normally write it down fully, but I opted for this to not make the lines too long.
Since we are going to be picking this way, we cannot simply pop_back from units_in_group. Instead we will erase the one we picked.
var paths:Array = get_children()
for item in groups:
var group := item as Group
var units_in_group := get_tree().get_nodes_in_group(group.name)
while not units_in_group.empty():
var path := paths.pop_back() as Path2D
var count := min(units_in_group.size(), group.max_count)
var separation := 1.0/count
var offset := separation * 0.5
for index in count:
var position := path.to_global(
path.interpolatef(
index * separation + offset
)
)
var best_distance := INF
var best_unit:Node2D = null
for unit in units_in_group:
var c_unit := unit as Node2D
var c_distance := c_unit.global_position.distance_to(position)
if c_distance < best_distance:
best_distance = c_distance
best_unit = c_unit
units_in_group.erase(best_unit)
best_unit.target = position
I'm tempted to refactor the selection of the best unit to a separate function. I submit it to your consideration.
Finally, thinking about the failure modes of this code: What comes to mind is that there might not be enough paths to position all the units. You can prevent that by setting a maximum number of units… Or you could write code to create paths as needed.
Hopefully I addressed everything that needed to be addressed.
I am facing a slice of numbers and want to create an ndarray::ArrayView out of this slice (without copying).
In the ArrayView, I want to include 2 numbers, then skip one, and so on. For example:
use ndarray::{ArrayView1, ShapeBuilder};
let numbers = &[0.0, 1.0, 10.0, 2.0, 3.0, 10.0];
let shape = // Some shape
let view = ArrayView1::from_shape(shape, numbers);
// Expected output for `view`:
// [0.0, 1.0, 2.0, 3.0]
Is there any way to set shape reaching the desired output?
Normal strides didn't work because they use a fix step size.
I also tried creating a 2x3 ArrayView2 out of numbers, then splitting the last column, and then flattening the array, but this cannot be done without copying because splitting breaks the memory layout.
I hope that it's possible to accomplish what I am trying to do.
ArrayView1 is a type alias for ArrayBase. The definition for ArrayBase is:
pub struct ArrayBase<S, D>
where
S: RawData,
{
/// Data buffer / ownership information. (If owned, contains the data
/// buffer; if borrowed, contains the lifetime and mutability.)
data: S,
/// A non-null pointer into the buffer held by `data`; may point anywhere
/// in its range. If `S: Data`, this pointer must be aligned.
ptr: std::ptr::NonNull<S::Elem>,
/// The lengths of the axes.
dim: D,
/// The element count stride per axis. To be parsed as `isize`.
strides: D,
}
D is essentially a list, so ArrayBase stores a list of dimensions and a list of strides. For ArrayView1, D basically ends up as [usize; 1], so you can only store a single length and stride.
To answer your question, sadly what you are asking for is not possible with ArrayView1, it lacks the storage space to hold multiple strides.
I want to use the result of two traversals a, b to calculate the value for an property. I don't know how to join them into combinedTraversal with both traversals starting from the origin element/vertex.
Graph graph = TinkerFactory.createModern();
GraphTraversal a = __.values("age").count().as("count")
.constant(10).as("allCount")
.math("count / allCount")
// .as("a");
GraphTraversal b = __.values("age").count().as("count")
.constant(10).as("allCount")
.math("count / allCount")
// .as("b");
GraphTraversal combinedTraversal = __
.local(a).as("a")
.local(b).as("b")
.math("a * b");
graph.traversal().V()
.has("age")
.property(Cardinality.single, "value", combinedTraversal)
.iterate();
In the example, traversal a and b assume to start at a given vertex and calculate their value. When i'm using local() the position of the traversal is changed and I don't know how to step back to the origin.
Using sideEffect() on the other hand does not refer to the current element, instead calculating the values for all elements into a map.
Is there any way to build such a combinedTraversal? (The "complex" calculation traversals are chosen on purpose, of course there are simpler ways to solve this example)
Thank You.
I found a solution: projection https://tinkerpop.apache.org/docs/current/reference/#project-step
GraphTraversal combinedTraversal = __.project("a", "b").by(a).by(b).math("a * b");
I would like to compute the phase of a complex number using boost::compute
here is my attempt, I expect the result to be equal to atan2(0.5f):
namespace bc = boost::compute;
bc::vector<std::complex<float>> vec{ {1.0f, 2.0f} };
bc::vector<float> result(1);
bc::transform(vec.begin(), vec.end(), result.begin(), bc::atan2<float>());
but I get a compilation error claiming "Non-unary function invoked one argument"
boost::compute's atan2 would appear to be a binary function just like std::atan2.
I'm assuming you're trying to obtain the phase angle of your complex number? The standard C++ function for this would be std::arg() - I don't see this one being defined in boost::compute, though I might have missed it.
If arg() is indeed missing, you're quite right it's implemented via atan2 - you'll need to extract the imaginary (boost::compute::imag()) and real (boost::compute::real()) components first though, and pass them as individual arguments to atan2.
I think you can also use Boost.Compute's lambda expressions for this:
bc::vector<float2> input{ {1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f} };
bc::vector<float> output(3);
using boost::compute::lambda::atan2;
using boost::compute::_1;
using boost::compute::lambda::get;
bc::transform(
float2_input.begin(),
float2_input.end(),
float_output.begin(),
atan2(get<1>(_1), get<0>(_1)),
queue
);
float2 is bassically a complex in Boost.Compute. You can also check test_lambda.cpp.
I found a way to make it work.
stage 1: allocate 2 vectors:
bc::vector<std::complex<float>> vec{ {1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f} };
bc::vector<float> result(3);
stage 2: interpret the complex vector as a float buffer iterator
buffer_iterator is quite useful when you have a strongly typed vector and would like to pass it to an algorithm as a different type.
auto beginf = bc::make_buffer_iterator<float>(vec.get_buffer(), 0);
auto endf = bc::make_buffer_iterator<float>(vec.get_buffer(), 6); // note end point to final index + 1
stage 3: define strided iterators so that we can use the same buffer as the argument for tan2. each iterator iterates the buffers in strides of 2 indices, and they supply tan2 with interleaved access to the buffer:
auto begin_a = bc::make_strided_iterator(beginf + 1, 2); // access imaginary part
auto end_a = bc::make_strided_iterator_end(beginf + 1, endf , 2);
auto begin_b = bc::make_strided_iterator(beginf, 2); // access real part
finally, call transform:
bc::transform(begin_a, end_a, begin_b, result.begin(), bc::atan2<float>()); // atan(b/a)
bc::system::default_queue().finish();
I have created a Vector3() called ori, and I have populated its coordinates x, y and z. How, now, do I translate this vector, say along axis z, of the indicated value?
I tried this:
ori.translateZ( - 100);
This gets me an error (TypeError: Cannot read property 'translateZ' of undefined)
Matey gave the answer you need, but didn't tell you why your method didn't work. ori is a Vector3 and not a Object3D. translateZ() is a method of the Object3D class, but not a method of the Vector3 class. If the position member of an Object3D class had been set to equal ori (position is a Vector3) then translateZ on that Object3D instance would have worked.
Your understanding is correct. The answer juagicre gave would only change the z value to-100
If you want to translate by a single axis, it's as simple as adding the translation value:
ori.z += -100;
If you want to translate by a vector, it's again very simple:
var trans = new THREE.Vector3(-100,-200,-300);
ori.add(trans);
ori.setZ(-100);
Vector3 documentation