GODOT: Positioning 3D Object at Screen Edge - godot

I want to place a plane mesh at the left edge of the screen. How do I calculate the position and set it using GDScript?

You can get the current Camera with get_viewport().get_camera().
And you can ask a Camera for its viewing volume with get_frustum(), which returns an array with the planes, in the following order: near, far, left, top, right, bottom.
Now, you say left edge… That would be the segment from the near-left-top intersection to the near-left-bottom intersection. So we use the intersect_3 method of the Plane class.
At viola:
var camera := viewport.get_camera()
var planes := camera.get_frustum()
var near_plane := planes[0]
var left_plane := planes[2]
var top_plane := planes[3]
var bottom_plane := planes[5]
var near_left_top := near_plane.intersect_3(left_plane, top_plane)
var near_left_bottom := near_plane.intersect_3(left_plane, bottom_plane)
There near_left_top and near_left_bottom would be Vector3, in global coordinates, that represent the left edge of the screen in space.
By the way you can ask a Plane for its normal, for example:
var left_normal := left_plane.normal
We can use that to build a Basis. Something like this (this might not be the way you want to arrange the axis):
var axis_y := (near_left_bottom - near_left_top).normalized()
var axis_z := left_normal
var axis_x := axis_y.cross(axis_z)
var basis := Basis(axis_x, axis_y, axis_z)
And then you just need a position to make a transform. For example (this might not be the position you want):
var origin := (near_left_bottom + near_left_top) * 0.5
var plane_transform := Transform(basis, origin)
And then set it as the global_transform of your MeshInstance or whatever.
I doubt this is exactly how you want it, but I hope I have given you enough information to adapt it to your needs.
By the way, I'll remind you that the simplest way to have something follow the Camera is to make it a child of the Camera.

Related

Godot smooth transition between players

I have a "parent" player scene, and I inherit scenes for each player. The parent player scene has a camera. When the game switches between players, one player turns off its camera, and the other player turns its camera on:
if state != State.ACTIVE:
# If this player is becoming active, also
# set camera current
state = State.ACTIVE
camera.current = true
else:
# If player is not becoming active,
# disable this players camera
camera.current = false
But players can be in different positions, so the camera "jumps" from one to the other. Can we do something more sophisticated, like set the new camera to the current position so the smooth setting can be used to handle the transition?
One idea is to do get_viewport().get_camera() to find the current position of the camera to try and sync the position of the current camera with the new camera that is about to turn on, but appears to not work for 2D scenes. CF: https://github.com/godotengine/godot/pull/38317
Sadly, as you found out, there is no way to get the current Camera2D in Godot 3.x. And you found the pull request that adds the feature to Godot 4.0.
What I'm going to suggest is to have one sole Camera2D, so that one is always the current one. And you can define Position2D inside your scenes that can serve as interpolation targets to move the Camera2D.
I have an script that I think will be useful for you (I made it to be RemoteTransform2D but backwards, it does push a transform, it pulls it), I call it anchor_transform_2d.gd:
tool
class_name AnchorTransform2D
extends Node2D
export var anchor_path:NodePath setget set_anchor_path
export var reference_path:NodePath setget set_reference_path
export var set_local_transform:bool
export(int, FLAGS, "x", "y") var translation_mode:int
export(int, FLAGS, "x", "y") var scale_mode:int
export var rotation_mode:bool
var _anchor:Node2D
var _reference:Node2D
func _physics_process(_delta: float) -> void:
if not is_instance_valid(_anchor) or Engine.editor_hint:
set_physics_process(false)
return
#INPUT
var input := _anchor.global_transform
if is_instance_valid(_reference):
input = _reference.global_transform.affine_inverse() * input
#TRANSLATION
var origin := Vector2 (
input.origin.x if translation_mode & 1 else 0.0,
input.origin.y if translation_mode & 2 else 0.0
)
#ROTATION
var angle := 0.0
if rotation_mode:
angle = input.get_rotation()
#SCALE
var source_scale = input.get_scale()
var scaling := Vector2 (
source_scale.x if scale_mode & 16 else 1.0,
source_scale.y if scale_mode & 32 else 1.0
)
#RESULT
_set_target_transform(
Transform2D(angle, origin) * Transform2D.IDENTITY.scaled(scaling)
)
func set_anchor_path(new_value:NodePath) -> void:
anchor_path = new_value
if not is_inside_tree():
yield(self, "tree_entered")
_anchor = get_node_or_null(anchor_path) as Node2D
set_physics_process(is_instance_valid(_anchor) and not Engine.editor_hint)
if Engine.editor_hint:
update_configuration_warning()
func set_reference_path(new_value:NodePath) -> void:
reference_path = new_value
if not is_inside_tree():
yield(self, "tree_entered")
_reference = get_node_or_null(reference_path) as Node2D
func _set_target_transform(new_value:Transform2D) -> void:
if set_local_transform:
transform = new_value
return
global_transform = new_value
func _get_configuration_warning() -> String:
if _anchor == null:
return "Anchor not found"
return ""
Add this attached to a Node2D in anchor_path set the target from which you want to pull the transform (anchor_path is a NodePath, you can set to it something like $Position2D.get_path()). And set what do you want to copy (you can choose any combination of position x, position y, scaling x, scaling y, and rotation). Then put the Camera2D as a child of the AnchorTransform2D, and set smoothing_enabled to true.
Rundown of the properties:
anchor_path: A NodePath pointing to the Node2D you want to pull the transform from.
reference_path: A NodePath pointing to a Node2D used to make the transform relative (you will be taking the transform of what you put in anchor_path relative to what you put in reference_path).
set_local_transform: Set to true if you want to pull the transform as local (relative to the parent of AnchorTransform2D), leave to false to set the global transform instead.
translation_mode: Specifies if you are going to copy the x position, y position, both or neither.
scale_mode: Specifies if you are going to copy the x scale, y scale, both or neither.
rotation_mode: Specifies if you are going to copy the rotation or not.
The only reason the script is a tool script is to give you a warning in the editor if you forgot to set the anchor_path.

Godot - Change tiles within an area

I'm trying to make it so if a function is called an area covered by a Collisionshape2D gets its tiles removed in a TileMap node. My problem is the area deleted doesn't match the Collisionshape2D's. Any insight is appreciated! Thank you.
My code:
func changeArea(collionshape):
var corridorTile = $CorridorTiles
var rect = Rect2(collionshape.position, collionshape.shape.extents*2)
var topleft = corridorTile.world_to_map(rect.position)
var bottomright = corridorTile.world_to_map(rect.end)
for x in range(topleft.x, bottomright.x):
for y in range(topleft.y, bottomright.y):
corridorTile.set_cell(x, y, -1)
Edit1*
Upon changing the code to:
func changeCorridorTile(collionshape):
var corridorTile = $CorridorTiles
var extents:Vector2 = collionshape.shape.extents
var topleft = corridorTile.world_to_map(collionshape.position - extents)
var bottomright = corridorTile.world_to_map(collionshape.position + extents)
for x in range(topleft.x, bottomright.x):
for y in range(topleft.y, bottomright.y):
corridorTile.set_cell(x, y, -1)
corridorTile.update_bitmask_region()
The cells in the tileMap which the collisionShape2D area covers get deleted. And I update them using .bitmask_region() method.
First thing I notice, from the top of my head: The Rect2 position is a corner, but the CollisionShape position is the center.
You would have to do it like this:
var extents:Vector2 = collionshape.shape.extents
var rect := Rect2(collionshape.position - extents, extents * 2)
Now, on a second look, you don't need the Rect2 at all:
var extents:Vector2 = collionshape.shape.extents
var topleft = corridorTile.world_to_map(collionshape.position - extents)
var bottomright = corridorTile.world_to_map(collionshape.position + extents)
One more thing: I believe that is in local space of the parent of the CollisionShape2D (an Area2D, I guess). So, if that is not working, it might be because you need to do it in global space. Hopefully you don't have any rotation (which would just mess the whole thing up) or scaling, so that would only be a matter of using global_position.
Addendum: I forgot something, world_to_map does not take global coordinates. Try this instead (assuming no rotation or scaling):
var extents:Vector2 = collionshape.shape.extents
var position := corridorTile.to_local(collionshape.global_position)
var topleft = corridorTile.world_to_map(position - extents)
var bottomright = corridorTile.world_to_map(position + extents)

Phaser P2 Collision kill colliding sprite

I was hoping that I could get some help with the problem that I'm having right now.
Basically I have some bullets, and then I have a group of enemies that move with velocity. The enemies have different sprites and basically I want to assign a value per sprite type. It works, for example, if I say if a diamond is generated and collides with a bullet you get 10 points, but the strange behavior is when it collides, since all enemies on screen give the 10 points and they all get destroyed, not just the one.
Also if the 10 point sprite is not on screen, no points are given which is normal.
Please find my code below and I would appreciate any help given. Thanks.
//here is my bullets group
createBullets: function(){
//Bullets
this.bullets = this.add.group();
this.bullets.enableBody = true;
this.bullets.physicsBodyType = Phaser.Physics.P2JS;
this.bullets.createMultiple(500, 'bullet', 0, false);
this.bullets.setAll('anchor.x', 0.5);
this.bullets.setAll('anchor.y', 0.5);
this.bullets.setAll('outOfBoundsKill', true);
this.bullets.setAll('checkWorldBounds', true);
},
///here are my enemies
addOneFigura: function(x, y) {
this.figuras = this.add.group();
//these are sprites
this.figuritas = ["figura1","figura2","figura3","figura4","figura5","figura6"];
this.figurita = this.figuritas[Math.floor(Math.random()*6)];
this.figura = this.add.sprite(x,y,this.figurita);
this.figuras.add(this.figura);
this.physics.p2.enable(this.figura, false);
this.figura.body.velocity.y = 75;
this.figura.checkWorldBounds = true;
this.figura.outOfBoundsKill = true;
this.figura.body.setCollisionGroup(this.figurasCG);
this.figura.body.collides(this.bulletsCG, this.collisionBulletMatch, this);
},
//and lastly this is my collision function
collisionBulletMatch: function(figurita, figuritapega) {
if (this.figurita != this.figuritapega){
this.score += 10;
this.scoreText.text = this.score;
this.resetFigura();
}
}
So basically when they collide the whole figuras group disappears instead of just the one colliding.
I'll readily admit I haven't done much with P2 physics in Phaser, but the immediate thing that comes to mind is that while you're calling resetFigura you're not passing in a figure (figura). Without seeing the initialization of the variable I can't be sure, but I think you want to change your function to something like the following:
resetFigura: function(figura) {
figura.sprite.kill();
},
This should be called via this.resetFigura(figuritapega); in collisionBulletMatch.
If it's not in the resetFigura call, I would also recommend taking a look at the official P2 Physics collision group example; I was able to get that up and running fairly quickly. It does have a single player character, but if you simplify your bullets to one you might be able to figure out your issue.

resize SVG Element: best practice with multiple items

What is the best practice for resizing say an Ellipse or Rectangle on another SVG element?
If I check for the onMouseMove event on the ellipse when I am out of it, the resizing stops. I was thinking to implement the listener on the svg element as well and pass the information to the ellipse I started the resizing on. This means having a generic Resize() method on the svg element that passes to the Resize() of the selected ellipse.
Isn't there an easier way?
I'm currently doing it with Dart, but it should be the same with javascript.
An example is an <svg> element with a <g> containing <rect> and an <ellipse>. If I start to resize the rectangle on a rectangle.onMouseMove, the moment I'm outside it, it stops resizing. To avoid this, I have to use the svg.onMouseMove, and use a resize method that resizes the rectangle. To know that it's the rectangle to be resized, I check for onMouseDown and again check the target (MouseEvent.target on Dart. Not sure how to detect what I'm touching without doing a cumbersome check on id maybe). Note that what I am trying to accomplish is to use the rectangle to resize the ellipse. I'm showing the rectangle only when resizing.
The following code will create a circle and resize when you start dragging.
It also works when the mouse gets outside the circle.
Maybe you can modify this to your liking.
Oh, and my Dart-code might not be the best, as I just recently started learning Dart.
Any improvements are welcome.
import 'dart:html';
import 'dart:svg';
import 'dart:math' as math;
void main() {
// add new svg
var svg = new SvgSvgElement();
svg.style.width = '400px';
svg.style.height = '400px';
svg.style.border = '1px solid black';
var body = document.querySelector('body');
body.append(svg);
// add group
var g = new SvgElement.tag('g');
svg.append(g);
// center of circle
var center = new math.Point(200, 200);
var startR = 70;
var newR = 70;
// add circle
var circle = new CircleElement();
circle.setAttribute('cx', center.x.toString());
circle.setAttribute('cy', center.y.toString());
circle.setAttribute('r', startR.toString());
circle.setAttribute('fill', 'green');
g.append(circle);
circle.onMouseDown.listen((MouseEvent E) {
var startOffset = E.offset;
var startDistance = startOffset.distanceTo(center);
math.Point offset = E.offset;
// now start listening for document movements so we don't need to stay on the circle
var move = document.onMouseMove.listen((MouseEvent E) {
// calculate new position
var movement = E.movement;
offset = new math.Point(
// multiply with 0.5 to make the mouse move faster than the circle grows
// that way we show that the mouse movement also works outside the circle element
offset.x + movement.x * 0.5,
offset.y + movement.y * 0.5
);
// calculate new distance from center
var distance = offset.distanceTo(center);
// calculate new radius for circle
newR = distance / startDistance * startR;
// resize circle
circle.setAttribute('r', newR.toString());
});
// and stop all listening on mouseup
var up = document.onMouseUp.listen(null);
up.onData((MouseEvent E) {
move.cancel();
up.cancel();
startR = newR;
});
});
}
Hope this helps,
Kind regards,
Hendrik Jan

Raphael -- object rotation at arbitrary position

I'm using Raphael_2.01 and would like to rotate an object at arbitrary position.
(WindowsXP, Firefox3.6)
example: http://uproda11.2ch-library.com/326446b6u/11326446.png
This rectangle (rect0) rotates thirty degrees at its lower right point.
The parameters are:
var rectX = rect0.getBBox().x;
var rectY = rect0.getBBox().y;
var rectW = rect0.getBBox().width;
var rectH = rect0.getBBox().height;
var rot = 30;// rotation
var rotX, rotY;// arbitrary position
What code should I use ? I can't image suitable method.
thanks,
If I understand the question correctly, it's rect0.rotate(30, rotX, rotY);.

Resources