Detecting click/touchscreen input on a 3d object inside godot - godot

I am trying my best to build a mobile game in the godot game engine. And i have run into a problem. In a 3D game, how can i detect a mouse click on a specific object or rather where that object is on the screen. I do not understand how to use the control nodes in order to to this.

The easiest way to make a 3D object clickable is to give it a CollisionObject (such as a StaticBody) and connect to the input_event signal. For example, to detect a left-click:
extends StaticBody
func _ready():
connect("input_event", self, "on_input_event")
func on_input_event(camera, event, click_position, click_normal, shape_idx):
var mouse_click = event as InputEventMouseButton
if mouse_click and mouse_click.button_index == 1 and mouse_click.pressed:
print("clicked")
The docs mention that touch events are similar to click events.
Note that input_ray_pickable must be true on the CollisionObject (this is the default).

Related

Godot: How to move KinematicBody2D node from one scene to another

I want to move my Player node (KinematicBody2D) from one scene to another. My code successfully moves Node2D node, but fails with KinematicBody2D node.
I have two Location scenes (inheriting from base Location scene) with the following structure:
FirstLocation (inherits Location)
- YSort
- Player
In the base Location scene I have two methods:
func add_player(player: Player):
get_node("YSort").add_child(player)
func remove_player() -> Player:
var player = get_node("YSort/Player")
get_node("YSort").remove_child(player)
return player
In GameWorld scene I store the possible locations inside a dictionary and the moving of the player happens inside change_location() function:
onready var _locations = {
Location.FOO: $CurrentLocation,
Location.BAR: load("res://locations/Bar.tscn").instance(),
}
onready var _current_location = $CurrentLocation
func change_location(location: int):
var player = _current_location.remove_player()
remove_child(_current_location)
_current_location = _locations[location]
add_child(_current_location)
_current_location.add_player(player)
The switching of the location works
The moving of the player also works in case the player is plain Node2D.
But when Player is KinematicBody2D then the game simply crashes, giving me no hint as to what's causing the problem.
The code works without crashing when I comment out the last line:
_current_location.add_player(player)
...but of course then the player simply doesn't get added to the other scene.
I verified that the player does get removed from the scene.
I tested with a dummy scene (which only contains KinematicBody2D as root node and a simple Sprite as a single child) instead of my actual more complex Player scene to make sure it's not related to any other code I might have in my Player scene. Node2D as root works, KinematicBody2D crashes. Must have been a fluke. Tested again and now both work, so there must be something different in my Player object.
I tried adding the Player node as a direct child of Location node (not having a YSort node in the middle) - nope, still crashes.
I tried setting the position/global_position of player before/after adding it to new scene - no difference.
I'm able to create new Player scene instance and add it to new location.
What might it be in KinematicBody2D that prevents me from moving it from scene to scene?
Found a solution, but I don't know why it works.
I was performing the location change as a response to Area2D.body_entered signal. I triggered my own signal "location_change" and then called the change_location() function in another part of the code in response to it.
Adding a tiny timeout (0.00000001 seconds) before doing the location change solved the issue. However I have no idea why, and I'm pretty sure adding timeouts is not a proper way to solve this problem.
I'm having trouble visualizing the situation. However, given that the problem happens when removing and adding a physics body, and that "body_entered" was involved, and that the solution you found was adding a time out…
Sounds like the issue was removing the physics body while Godot was still resolving physics response. And then the solution was to wait until the next frame.
You could wait until the next graphics frame with this:
yield(get_tree(), "idle_frame")
Or until the next physics frame with this:
yield(get_tree(), "physics_frame")
Which would be better than some arbitrary small timeout.
Alternatively, you could also make the signal connection deferred.
If you are connecting from the UI, there will be an "advanced" toggle in the "Connect a Signal to a Method" dialog. Enabling "advanced" toggle will reveal some extra including making the connection deferred.
If you are connecting form code, you can accomplish the same thing by passing the CONNECT_DEFERRED flag.

Why is my game scene getting errors when the main scene is actually the menu in Godot

I am adding a main menu to my game. The thing I am doing is adding them in a scene and then changing the scene to the game scene. However, my game has errors which belong to other scenes that do not have an instance in the existing scene. I get errors such as:
Invalid get index 'HasEntered' (on base: 'null instance').
My entire project is here: https://github.com/Ripple-Studios/Godot-Wild-Jam-36-Game-Ripple-Studios
I would appreciate if someone would help me.
Thanks,
I had a look at the linked code.
What is happening is that the practice of getting nodes of the parent has come back to bite you. I'm talking code that looks like this:
get_parent().get_node(...)
You have an scene (HospitalScene.tscn) instanced in your main scene (MainMenu.tscn), which has code similar to the shown above.
The particular error you refer to comes from code that looks like this:
func _on_Area2D_body_entered(body):
var HospitalPosition = get_parent().get_node("Hospital")
if HospitalPosition.HasEntered == true:
HospitalPosition.isInHospital = false
This code is firing at the start because the Area2D is overlapping an StaticBody.
The code is trying to get a sibling "Hospital" which does not exist in the scene tree. And thus HospitalPosition is null. And trying to access HospitalPosition.HasEntered when HospitalPosition is null results in the error you mention:
Invalid get index 'HasEntered' (on base: 'null instance').
The scene is trying to reach a node outside of itself. And there is no guarantee that the scene will be instanced where such node is available. Thus, in each and every case of get_parent().get_node(...) this could happen.
In fact, when running the game, I get four error that look like this (but with different paths):
E 0:00:01.494 get_node: (Node not found: "Hospital" (relative to "/root/CanvasLayer/ParentSprite/Control").)
<C++ Error> Condition "!node" is true. Returned: nullptr
<C++ Source> scene/main/node.cpp:1325 # get_node()
<Stack Trace> HospitalScene.gd:28 # _on_Area2D_body_entered()
You could use get_node_or_null instead of get_node and check for null.
You could also export NodePath variables instead of hard-coding the paths.
Better decoupling strategies than checking if instances are valid include:
Connect to the signal from outside the scene. It is a common pattern in Godot to call down the scene tree, and signal up the scene tree. See Node communication (the right way).
Connect all the signals through an Autoload (singleton). Which is another common pattern in Godot called a Signal Bus or Event Bus. See Best practices: Godot GDScript - Event Bus.

Do instanced objects Area 2D's not detect the mouse

I am trying to get a dynamically instanced kinematicBody2D with an area 2D attached to handle mouse entered/exit inputs. I have created my area 2D with correct collision body, and have tested a similar collision body for detecting some area 2d's and this is working happily, however, the mouse detection is not triggering the function as it should.
I am unsure of why it does not appear to be detecting my mouse. I am assuming I have messed with the Masks incorrectly, and it is not on the same level, however looking at some of the documentation this is not suggested to be a problem.
I am unsure of what code to attach because it is not really coded at this point.
Any help would be appreciated.
To detect mouse events on an Area or KinematicBody, set input_pickable to true and connect to one or more of the provided signals.
KinematicBody2D and Area2D both inherit from CollisionObject2D, so they can both handle mouse input. This means you don't need to add an Area to your KinematicBody unless the area that detects clicks needs to be different than the area that detects collisions (e.g. only a small part of a larger object is clickable).
Here's how you could detect mouse events on a KinematicBody with some CollisionShape:
func _ready():
input_pickable = true
connect("mouse_entered", self, "_on_mouse_entered")
connect("mouse_entered", self, "_on_mouse_entered")
connect("input_event", self, "_on_input_event")
func _on_mouse_entered():
print("mouse entered")
func _on_mouse_exited():
print("mouse exited")
func _on_input_event(viewport, input_event, shape_idx):
var mouse_event = input_event as InputEventMouseButton
if mouse_event:
prints("Mouse button clicked:", mouse_event.button_index)

Object Collision Issue - Weird Behaviour - Unity

I wondering if someone could give me a hand with this problem I'm having with objects and collisions in Unity.
I have a sphere object being controlled by the users phone's accelerometer. The sphere moves around fine but once it hits a wall the sphere starts acting weird. It pulls in the direction of the wall it collided with, starts bouncing, and just overall not responsive anymore to the movement of the phone.
Any idea as to why this could be happening?
Here is the script used to control the player's sphere.
using UnityEngine;
using System.Collections;
public class PlayerController : MonoBehaviour {
public float speed;
void Update() {
Vector3 dir = Vector3.zero;
dir.x = Input.acceleration.x;
dir.z = Input.acceleration.y;
if (dir.sqrMagnitude > 1)
dir.Normalize();
dir *= Time.deltaTime;
transform.Translate(dir * speed);
}
void OnTriggerEnter (Collider other)
{
if (other.gameObject.tag == "Pickup") {
other.gameObject.SetActive(false);
}
}
}
That happens because your object has a 'Rigidbody' component, and, I suppose, it's not a kinetic rigidbody. Basically, it behaves just like it should: a real physical object will not pass through another object, that is the most basic behaviour of a physics engine. However, since you don't operate with the physics-based object using forces, but manually change it's position, you break a level of abstraction. In result, you move the object inside the wall, and now it can't get out.
Use ApplyForce method instead. If you want to pull or push object (instead of just move, which contradicts the fact that these objects are managed by physics) in a certain direction every frame, you should use ForceMode.Acceleration (or ForceMode.Force, if you want the effect to depend on the mass) every physics frame, which means that you have to use FixedUpdate method instead of Update.

j2me screen flicker when switching between canvases

I'm writing a mobile phone game using j2me. In this game, I am using multiple Canvas objects.
For example, the game menu is a Canvas object, and the actual game is a Canvas object too.
I've noticed that, on some devices, when I switch from one Canvas to another, e.g from the main menu to the game, the screen momentarily "flickers". I'm using my own double buffered Canvas.
Is there anyway to avoid this?
I would say, that using multiple canvases is generally bad design. On some phones it will even crash. The best way would really be using one canvas with tracking state of the application. And then in paint method you would have
protected void paint(final Graphics g) {
if(menu) {
paintMenu(g);
} else if (game) {
paintGame(g);
}
}
There are better ways to handle application state with screen objects, that would make the design cleaner, but I think you got the idea :)
/JaanusSiim
Do you use double buffering? If the device itself does not support double buffering you should define a off screen buffer (Image) and paint to it first and then paint the end result to the real screen. Do this for each of your canvases. Here is an example:
public class MyScreen extends Canvas {
private Image osb;
private Graphics osg;
//...
public MyScreen()
{
// if device is not double buffered
// use image as a offscreen buffer
if (!isDoubleBuffered())
{
osb = Image.createImage(screenWidth, screenHeight);
osg = osb.getGraphics();
osg.setFont(defaultFont);
}
}
protected void paint(Graphics graphics)
{
if (!isDoubleBuffered())
{
// do your painting on off screen buffer first
renderWorld(osg);
// once done paint it at image on the real screen
graphics.drawImage(osb, 0, 0, Tools.GRAPHICS_TOP_LEFT);
}
else
{
osg = graphics;
renderWorld(graphics);
}
}
}
A possible fix is by synchronising the switch using Display.callSerially(). The flicker is probably caused by the app attempting to draw to the screen while the switch of the Canvas is still ongoing. callSerially() is supposed to wait for the repaint to finish before attempting to call run() again.
But all this is entirely dependent on the phone since many devices do not implement callSerially(), never mind follow the implementation listed in the official documentation. The only devices I've known to work correctly with callSerially() were Siemens phones.
Another possible attempt would be to put a Thread.sleep() of something huge like 1000 ms, making sure that you've called your setCurrent() method beforehand. This way, the device might manage to make the change before the displayable attempts to draw.
The most likely problem is that it is a device issue and the guaranteed fix to the flicker is simple - use one Canvas. Probably not what you wanted to hear though. :)
It might be a good idea to use GameCanvas class if you are writing a game. It is much better for such purpose and when used properly it should solve your problem.
Hypothetically, using 1 canvas with a sate machine code for your application is a good idea. However the only device I have to test applications on (MOTO v3) crashes at resources loading time just because there's too much code/to be loaded in 1 GameCanvas ( haven't tried with Canvas ). It's as painful as it is real and atm I haven't found a solution to the problem.
If you're lucky to have a good number of devices to test on, it is worth having both approaches implemented and pretty much make versions of your game for each device.

Resources