I'm facing a problem where an entity already exists, but I want to add a PbrBundle to it.
To try solve this I'm attempting to spawn a child entity along with the PbrBundle. Hoping that it will be transformed according to the parent, but the transformation part doesn't seem to work out.
fn spawn_parent(commands: &mut Commands) -> Entity {
let entity = commands.spawn((Transform::from_translation(vec3(1.0, 0.0, 0.0)),)) ...
I'm expecting the line drawn by the code to not sit in the middle of the screen (should be translated to the right, changing the parent's doesn't seem to have any effect at all):
I get the following output from the code below:
Spawned camera: 0v0
Spawned parent: 1v0
Spawned child: 2v0
0v0 changed global transform: Vec3(0.0, 0.0, 10.0)
2v0 changed parent: Parent(1v0)
1v0 changed children: Children([2v0])
2v0 changed global transform: Vec3(0.0, 0.0, 0.0)
The code:
use bevy::prelude::*;
use bevy::math::vec3;
use bevy::render::mesh::Indices;
use bevy::render::pipeline::PrimitiveTopology;
fn main() {
App::build()
.add_plugins(DefaultPlugins)
.add_system(debug_global_transform.system())
.add_system(debug_children.system())
.add_system(debug_parent.system())
.add_startup_system(setup.system())
.run();
}
fn setup(commands: &mut Commands, meshes: ResMut<Assets<Mesh>>, materials: ResMut<Assets<StandardMaterial>>) {
spawn_camera(commands);
let parent_entity = spawn_parent(commands);
spawn_child(commands, meshes, materials, parent_entity);
}
fn spawn_camera(commands: &mut Commands) {
commands.spawn(Camera3dBundle {
perspective_projection: Default::default(),
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0))
.looking_at(Vec3::zero(), Vec3::unit_y()),
..Default::default()
});
println!("Spawned camera: {:?}", commands.current_entity().unwrap());
}
fn spawn_parent(commands: &mut Commands) -> Entity {
let entity = commands.spawn((Transform::from_translation(vec3(1.0, 0.0, 0.0)),)).current_entity().unwrap();
println!("Spawned parent: {:?}", entity);
entity
}
fn spawn_child(commands: &mut Commands, mut meshes: ResMut<Assets<Mesh>>, mut materials: ResMut<Assets<StandardMaterial>>, parent_entity: Entity) {
let mut mesh = Mesh::new(PrimitiveTopology::LineList);
mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vec![[0.0, 0.0, 0.0], [0.0, 1.0, 0.0]]);
mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]);
mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, vec![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]]);
mesh.set_indices(Some(Indices::U32(vec![0, 1])));
let child_entity = commands.spawn(PbrBundle {
mesh: meshes.add(mesh),
material: materials.add(Color::rgb(1.0, 1.0, 1.0).into()),
// Default transform, let the parent decide where this is drawn
..Default::default()
})
//.with(Parent(parent_entity)) // Doesn't seem to work well either
.current_entity()
.unwrap();
println!("Spawned child: {:?}", child_entity);
commands.push_children(parent_entity, &[child_entity]);
}
fn debug_global_transform(query: Query<(Entity, &GlobalTransform), Changed<GlobalTransform>>) {
query.iter()
.for_each(|(entity, transform)| {
println!("{:?} changed global transform: {:?}", entity, transform.translation);
});
}
fn debug_children(children_query: Query<(Entity, &Children), Changed<Children>>) {
for (entity, children) in children_query.iter() {
println!("{:?} changed children: {:?}", entity, children);
}
}
fn debug_parent(parent_query: Query<(Entity, &Parent), Changed<Parent>>) {
for (entity, parent) in parent_query.iter() {
println!("{:?} changed parent: {:?}", entity, parent);
}
}
Seems like adding a GlobalTransform to the parent resolves the issue :)
fn spawn_parent(commands: &mut Commands) -> Entity {
let entity = commands.spawn((GlobalTransform::default(), Transform::from_translation(vec3(1.0, 0.0, 0.0)))).current_entity().unwrap();
println!("Spawned parent: {:?}", entity);
entity
}
Related
I am fairly new to Bevy and rust. I want to load an png image and get the width and height of it. The code below does not print "found resource ...".
fn setup( mut commands: Commands,
asset_server: Res<AssetServer>,
mut materials: ResMut<Assets<Image>>) {
let texture_handle = asset_server.load("board.png");
//materials.add(texture_handle.clone().into()); //Gives error: the trait `From<Handle<_>>` is not implemented for `bevy::prelude::Image`
commands.spawn().insert_bundle(SpriteBundle {
texture: texture_handle.clone(),
..Default::default()
}).insert(BackgroundSprite);
if let Some(image) = materials.get(texture_handle) {
print!("found resource with width and height: [{},{}]", image.texture_descriptor.size.width, image.texture_descriptor.size.height);
}
}
I figured it out after some help on the Bevy Discord help channle.
Resources are loaded asynchronously and has to be accessed at a later point. See AssetEvent example here
I am a beginner when it comes to Rust so I wont say this is the way to do it. But here is my result:
#[derive(Component)]
pub struct BackgroundHandle {
handle: Handle<Image>,
}
#[derive(Component)]
pub struct BackgroundSpriteSize {
width: u32,
height: u32,
}
fn setup( mut commands: Commands,
mut app_state: ResMut<State<BoardState>>,
asset_server: Res<AssetServer>) {
let texture_handle = asset_server.load("board.png");
commands.spawn().insert_bundle(SpriteBundle {
texture: texture_handle.clone(),
..Default::default()
}).insert(BackgroundSpriteBundle);
commands.insert_resource(BackgroundHandle {
handle: texture_handle,
});
app_state.set(BoardState::Initializing);
}
fn setupBounds(mut commands: Commands,
mut app_state: ResMut<State<BoardState>>,
mut ev_asset: EventReader<AssetEvent<Image>>,
assets: Res<Assets<Image>>,
bg: Res<BackgroundHandle>) {
for ev in ev_asset.iter() {
match ev {
AssetEvent::Created { handle } => {
if *handle == bg.handle {
let img = assets.get(bg.handle.clone()).unwrap();
let bg_size = BackgroundSpriteSize {
width: img.texture_descriptor.size.width,
height: img.texture_descriptor.size.height,
};
commands.insert_resource(bg_size);
app_state.set(BoardState::Initialized);
}
},
_ => {
}
}
}
}
Consider this dataset:
[
{
PartId: 0,
Positions: [
{ x: 0, y: 0, z: 0, timeCode: 0.0000 },
{ x: 0, y: 0, z: 1, timeCode: 0.0025 },
{ x: 0, y: 0, z: 2, timeCode: 0.0050 },
{ x: 0, y: 0, z: 3, timeCode: 0.0075 },
{ x: 0, y: 0, z: 4, timeCode: 0.0100 },
{ x: 0, y: 0, z: 5, timeCode: 0.0125 },
{ x: 0, y: 0, z: 6, timeCode: 0.0150 },
{ x: 0, y: 0, z: 7, timeCode: 0.0175 },
{ x: 0, y: 0, z: 8, timeCode: 0.0200 },
]
},
{
PartId: 1,
Positions: [
{ x: 0, y: 0, z: 0, timeCode: 0.0000 },
{ x: 0, y: 0, z: 2, timeCode: 0.0025 },
{ x: 0, y: 0, z: 4, timeCode: 0.0050 },
{ x: 0, y: 0, z: 6, timeCode: 0.0075 },
{ x: 0, y: 0, z: 8, timeCode: 0.0100 },
{ x: 0, y: 0, z: 10, timeCode: 0.0125 },
{ x: 0, y: 0, z: 12, timeCode: 0.0150 },
{ x: 0, y: 0, z: 14, timeCode: 0.0175 },
{ x: 0, y: 0, z: 16, timeCode: 0.0200 },
]
}
]
I am able to load and parse the JSON data and I can create a new GameObject for each PartId. I am trying to figure out the best way to transform each "Part" according to the Position collection all at the same time.
I have an empty GameObject in the scene with an attached class. Inside the Start method I get the JSON data and then in a loop I create the GameObject class, set its initial position and then start a coroutine defined in the same class.
Main class:
void Start() {
// do json stuff...
// ........
// then
// for each part...
foreach(PartGroup pg in Parts) {
// create a new GameObject from the Part class
Part part = gameObject.AddComponent(typeof(Part)) as Part;
// send this part data to the new GameObject
part.PartGroup = pg;
// set the initial position for the part
part.Init();
// start a IEnumerator Coroutine in the part class
StartCoroutine(part.PlayFrom());
}
}
Part class:
public void Init() {
Joint = GameObject.CreatePrimitive(PrimitiveType.Sphere);
Joint.transform.localScale = new Vector3(jointSize, jointSize, jointSize);
Joint.transform.position = new Vector3(PartGroup.Positions[0].X, PartGroup.Positions[0].Z, PartGroup.Positions[0].Y);
}
public IEnumerator PlayFrom(float time = 0f) {
while (PlayBack(time)) {
yield return null;
time += Time.fixedDeltaTime;
}
}
bool PlayBack(float time) {
float sample = time / (Time.fixedDeltaTime / speed);
int previousIndex = (int)(sample);
int last = PartGroup.Positions.Count - 1;
if (previousIndex < last) {
int nextIndex = previousIndex + 1;
float interpolation = sample - previousIndex;
Joint.transform.position = Vector3.Lerp(
new Vector3(PartGroup.Positions[previousIndex].X, PartGroup.Positions[previousIndex].Z, PartGroup.Positions[previousIndex].Y),
new Vector3(PartGroup.Positions[nextIndex].X, PartGroup.Positions[nextIndex].Z, PartGroup.Positions[nextIndex].Y),
interpolation);
return true;
}
Joint.transform.position = new Vector3(PartGroup.Positions[last].X, PartGroup.Positions[last].Z, PartGroup.Positions[last].Y);
return false;
}
This is how I currently have it set up. It does work, but it is not smooth motion and sometimes it seems to lag or jump frames. Is this the best way to accomplish this, or is there a better way (like FixedUpdate)? I have set the fixed time property in my project settings to match the data timeCode.
Any help with best practices for stuff like this is greatly appreciated!
You have to use Time.deltaTime in
time += Time.deltaTime;
and
float sample = time / (Time.deltaTime / speed);
Coroutines are executed together with all Update calls so using fixedDeltaTime breaks the frame independency.
or probably use WaitForFixedUpdate to execute it like FixedUpdate
while (PlayBack(time)) {
yield return new WaitForFixedUpdate();
time += Time.fixedDeltaTime;
}
Also in
foreach(PartGroup pg in Parts)
{
// create a new GameObject from the Part class
Part part = gameObject.AddComponent(typeof(Part)) as Part;
// send this part data to the new GameObject
part.PartGroup = pg;
// set the initial position for the part
part.Init();
// start a IEnumerator Coroutine in the part class
StartCoroutine(part.PlayFrom());
}
it looks like you are adding a component for each element in the list all to one and the same GameObject .. I don't know if that's what you planned to do. AddComponent does not create a new GameObject with that component but attaches that component to the same gameObject that script is attached to.
You probably ment to use new GameObject
Part part = new GameObject("new GameObject").AddComponent<Part>();
// make it a child of this gameObject?
part.SetParent(gameObject, false);
also the calculations
float sample = time / (Time.fixedDeltaTime / speed);
int previousIndex = (int)(sample);
...
float interpolation = sample - previousIndex;
seems a bit odd .. are you sure it always returns a value between 0 and 1?
I have an item (a rectangle) which can be moved through the different positions of the grid (see code below).
The reference to decide whether the rectangle is in one cell or another is the top-left (x=0;y=0) of the rectangle. I want the reference to be the center of the rectangle. Any ideas?
Find the code below:
import QtQuick 2.5
import QtPositioning 5.5
import QtLocation 5.6
import QtGraphicalEffects 1.0
import QtQml 2.2
import QtQuick.Controls 1.0
Rectangle{
id: mainContainer
width:800; height:width
color: "red"
Grid {
id:gridA
anchors.centerIn: parent
width: parent.width; height:width;
columns: 2; rows: 2; spacing: 2
verticalItemAlignment: Grid.AlignHCenter
horizontalItemAlignment: Grid.AlignVCenter
Repeater {
id:cells
model: gridA.columns*gridA.rows
property int draggedItemIndex: -1
Rectangle {
width: gridA.width/gridA.columns; height: gridA.height/gridA.rows
color: "white"
DropArea {
id: dragTarget
property alias dropProxy: dragTarget
anchors.fill: parent
keys: [ "KEY_A" ]
Rectangle {
id: dropRectangle
anchors.fill: parent
states: [
State {
when: dragTarget.containsDrag
PropertyChanges { target: dropRectangle; color: "grey"}
PropertyChanges { target: cells; draggedItemIndex: index}
}
]
}
}
}
}
}
Rectangle{
id: icon
property var draggedIndex: 0
width: parent.width/3; height:width
color:"blue"
Drag.active: dragArea.drag.active
Drag.keys: [ "KEY_A" ]
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: icon
cursorShape: drag.active ? Qt.ClosedHandCursor : Qt.OpenHandCursor
onReleased: {
console.log("Drag: " + cells.draggedItemIndex)
if (cells.draggedItemIndex != icon.draggedIndex) {
icon.draggedIndex = cells.draggedItemIndex
cells.draggedItemIndex = -1
}
icon.x = cells.itemAt(icon.draggedIndex).x
icon.y = cells.itemAt(icon.draggedIndex).y
}
}
}
}
You have the Drag-attached property, which has the hotSpot-property of type PointF.
Set this property to Qt.point(width / 2, height / 2)
I am using the playback plugin and i am using it on an image overlay.
https://github.com/hallahan/LeafletPlayback
I need to scale the floor map before placing the marker. with the plugin the marker is placed some where outside of the floor map.
I am able to solve the issue for GPS tracking, where i have written a function to scale the map and place the marker inside pointToLayer method of layer property.
I want to do the same for marker too. any help is appreciated.
const playbackOptions = {
playControl: true,
dateControl: true,
orientIcons: true,
fadeMarkersWhenStale: true,
// layer and marker options
layer: {
pointToLayer(featureData, latlng) {
const { lat, lng } = latlng;
let result = {};
if (featureData && featureData.properties && featureData.properties.path_options) {
result = featureData.properties.path_options;
}
if (!result.radius) {
result.radius = 5;
}
const scaleX = width / details.width;
const scaleY = height / details.length;
const m = {
x: lat * scaleX,
y: lng * scaleY,
};
const iconCls = 'asset-icon';
const item = L.marker(self.map.unproject([m.x, m.y], self.map.getMaxZoom()), {
icon: makeMarker(iconCls, 0),
opacity: 0.9,
type: 'asset',
lat,
lng,
});
item.bindTooltip(`<p>${lat}, ${lng}`, { className: 'asset-label', offset: [0, 0] });
return item;
}
},
marker: {
getPopup(featureData) {
let result = '';
if (featureData && featureData.properties && featureData.properties.title) {
result = featureData.properties.title;
}
return result;
}
}
};
If you retrieve actual GPS coordinates, it would probably be easier to actually do the reverse, i.e. to georeference your image overlay once for good, instead of trying to fiddle with the geographic coordinates of each of the feature you try to show relatively to your image.
Here's an example from Official Guide by Apple:
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) {
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// prints "square.origin is now at (10.0, 10.0)”
This make me pretty confused why we need get/set here, even we actually can do without them, then I tried:
struct anotherRect {
var origin = Point()
var size = Size()
var center: Point{
return Point(x: (origin.x + size.width/2), y: (origin.y + size.height/2))
}
}
var anotherSquare = Rect(origin: Point(x: 20.0, y: 20.0), size: Size(width: 10.0, height: 10.0))
println("anotherSquare.center is (\(anotherSquare.center.x), \(anotherSquare.center.y))")
// prints "anotherSquare.center is (25.0, 25.0)"
anotherSquare.center = Point(x: 30.0, y: 30.0)
println("anotherSquare.origin is now at (\(anotherSquare.origin.x), \(anotherSquare.origin.y))")
// prints "anotherSquare.origin is now at (25.0, 25.0)"
With this, I can do get value or set a new value to the computed properties, exactly the same get/set by Apple's way. Do we really need get/set in our code? and Why?
I'm just a newcomer, so feel free let me know all about this stuff? I really appriciate your help!!
They are not the same. If you don't specify the get set keywords, you only implement the getter.
struct AnotherRect {
var origin = Point()
var size = Size()
var center: Point {
return Point(x: (origin.x + size.width/2), y: (origin.y + size.height/2))
}
}
is equivalent to
struct AnotherRect {
var origin = Point()
var size = Size()
var center: Point {
get {
return Point(x: (origin.x + size.width/2), y: (origin.y + size.height/2))
}
}
}
Your example got you confused because you have an error in your code:
var anotherSquare = Rect(origin: Point(x: 20.0, y: 20.0), size: Size(width: 10.0, height: 10.0))
should be
var anotherSquare = AnotherRect(origin: Point(x: 20.0, y: 20.0), size: Size(width: 10.0, height: 10.0))
Try that and you will notice that you cannot assign to the center property.
Also, the convention is that you name your custom types starting with a capital letter, e.g.: AnotherRect.