Detecting if an enemy was shot by bullet - game-engine

I am trying to learn Godot by making a simple 2D shooter, but am running into a problem. I do not know how to detect if an enemy has been shot by a bullet. My bullets are Area2D nodes with a Sprite, and CollisionShape2D node attached. My enemies are KinematicBody2D nodes with a Sprite, and CollisionShape2D node attached. The enemy also has a timer attached, but that is not important.
Here is the enemy code:
extends KinematicBody2D
var speed = 200
var velocity = Vector2(-0.5, 1)
var bullet_preload = preload("res://Enemy/EnemyBullet.tscn")
func _ready():
randomize()
velocity.y = [1, -1][randi() % 2]
func _physics_process(delta):
var collision = move_and_collide(velocity * speed * delta)
if collision:
velocity = velocity.bounce(collision.normal)
if position.x < -20:
print("Enemy Despawned")
queue_free()
if $ShootDelay.time_left == 0:
var bullet = bullet_preload.instance()
bullet.position.x = position.x - 18
bullet.position.y = position.y + 6
get_parent().add_child(bullet)
$ShootDelay.start()
Here is my player bullet code:
extends Area2D
var speed = 600
var velocity = Vector2(1, 0)
func _process(delta):
position.x += speed * delta
if position.x > 1044:
queue_free()
func _on_PlayerBullet_body_entered(body):
queue_free()
It may be important to note that the player bullet and enemy scenes are not in the same scene tree, and there is no way (that I know of) of sending a signal between them. They are both being instanced to my main root node that contains the entire game via code.
If I need to include other details, please let me know.

On your _on_PlayerBullet_body_entered you a body that has a reference to whatever the Area2D collided with, you can use it to communicate with it.
For example, you can call a method on it. Which could, of course could be queue_free:
func _on_PlayerBullet_body_entered(body):
body.queue_free()
queue_free()
However, I'm going to suggest against it. Instead let us come up with a method that you can implement to have whatever behavior you want:
func _on_PlayerBullet_body_entered(body):
body.shot()
queue_free()
Of course, we have the problem that we are not sure if it has that method… So, let us check for it:
func _on_PlayerBullet_body_entered(body):
if body.has_method(shot):
body.shot()
queue_free()
Then you would add that method on the objects that react to the bullet impact. Back to queue_free, it could be:
func shot() -> void:
queue_free()
Or you could have it be something like this:
var health := 5
func shot() -> void:
health -= 1
if health <= 0:
queue_free()
Or, you could have it set a flag and deal with it on _physics_process or where and whenever appropiate:
var was_shot := 5
func _physics_process(_delta) -> void:
if was_shot:
queue_free()
func shot() -> void:
was_shot = true
Of course, aside form queue_free you may want to instance some other scene (e.g. a corpse or an explosion) or play an animation or something like that.

Related

Godot/GDScript Grid Movement Tweening

So I've been using KidsCanCode's Godot tutorials/documentation to help me create a Pokemon-like grid-based movement for a project I'm working on. For all intents and purposes, I would like to create a movement system as close to that in the earlier handheld Pokemon games as possible.
I would like to add two things before I start; one, I have grown fond of the way KidsCanCode attempted to teach grid-based movement, so while other ways of coding it may be simpler such as those that can be found on videos such as this one (https://www.youtube.com/watch?v=jSv5sGpnFso), I would like to hard-headidly stick to this method of coding it... you'll see what I mean when you read the code. Lastly, I would like to add that I had this code working before ! I actually haven't made any changes to the code since it was last working, however, for some reason it no longer seems to work, I'm not sure if that's due to Godot updating since, but hopefully someone can help me out with that.
So first of all, this is my player scene node tree. The most important parts of this being the RayCast2D and Tween nodes.
And this is my code for the main Area2D Player node:
extends Area2D
const tile_size = 16
export var speed = 5
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
func _ready():
position = position.snapped(Vector2.ONE * tile_size/2)
func _unhandled_input(event):
if $Tween.is_active():
return
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(inputs[dir])
func move(dir):
$RayCast2D.cast_to = inputs[dir] * tile_size
$RayCast2D.force_raycast_update()
if !$RayCast2D.is_colliding():
move_tween(dir)
func move_tween(dir):
$Tween.interpolate_property(self, "position", position,
position + inputs[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
$Tween.start()
To quickly explain, func _ready(): snaps the player to the grid. func _unhandled_input(event): then checks to see if a Tween is occurring, and if not, calls func move(dir). This function raycasts to the given direction input, forces a raycast update, and if no static body is in the given direction, calls func move_tween(dir). This last functions handles tween interpolation to the given direction and starts the tweening process. That's pretty much it. Once again, this used to work just fine.
However, now when I try to run this, I get an error "Invalid get index '(0, 1)' (on base: 'Dictionary')" where "(0, 1)" changes based on what direction I tried to move in when the game was running.
In the Debugger dock, underneath Stack Frames, it gives me errors on lines "22 - at function; move" $RayCast2D.cast_to = inputs[dir] * tile_size and "19 - at function: _unhandled_input" move(inputs[dir]).
The code on the website had these say (dir) only instead of (inputs[dir]). But doing so only gives me another error. If anyone smarter than me has any idea what's going on, I would very much appreciate any and all insight. Thank you !
Understanding the problem
Alright, let us see. The variable inputs has your dictionary:
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
The keys are String, and the values are Vector2.
Thus, here:
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(inputs[dir])
The variable dir is going to be a String. Which is what you need for is_action_pressed, so that is correct.
And inputs[dir] is going to be a Vector2. Which means that in move you are getting a Vector2 as argument.
Now, in move you say::
func move(dir):
$RayCast2D.cast_to = inputs[dir] * tile_size
But remember that the argument you are passing is a Vector2, and the keys of input are all String. So it fails here: inputs[dir].
Early warning for similar problems
Using types can help you identify this kind of problems early. Sadly in Godot 3.x there is no way to specify the the keys and values of a Dictionary.
Arguably you could use C# and use .NET Dictionary<TKey,TValue> from the System.Collections.Generic, which would let you specify the key and value types. Yet, we are not talking about those dictionaries here.
What you can tell with GDScript is that your parameters are either Vector2:
func move(displacement:Vector2):
# …
Or String
func move(dir:String):
# …
This way Godot can tell you when you are calling them with the wrong parameter.
Another thing that will help. Although it is more on the discipline side, is to keep consistent names. If the names you use have a concrete meaning in your system, they will help you.
For instance, you call move like this:
move(inputs[dir])
Meaning that what you are passing is not called dir※. But you have move defined like this:
func move(dir):
# …
So move expects something you call a dir. And you would see that when you are typing the call to move.
※: I'd say you are passing one of the values of inputs, so what you are passing is called an input. Or you could call them action, given that you use them in is_action_pressed. Which, again, would be using names in a way that helps you.
Solving the problem
The way I would solve this is by using the String and inputs in _unhandled_input only (after all, that function is meant to deal with inputs). And work with Vector2 from there on. This means that:
The other methods would also be useful if in the future you wanted a movement that does not come from one of the inputs.
You are not repeating the effort of looking up in the Dictionary.
Admittedly, these aren't a huge deal for your game right now. And ultimately what you do is up to you. Yet, consider this approach submitted to your consideration.
This is the code (I have added some type annotations):
extends Area2D
const tile_size:float = 16
export var speed:float = 5
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
func _ready():
position = position.snapped(Vector2.ONE * tile_size/2)
func _unhandled_input(event:InputEvent) -> void:
if $Tween.is_active():
return
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(inputs[dir])
func move(displacement:Vector2) -> void:
$RayCast2D.cast_to = displacement * tile_size
$RayCast2D.force_raycast_update()
if !$RayCast2D.is_colliding():
move_tween(displacement)
func move_tween(displacement:Vector2) -> void:
$Tween.interpolate_property(self, "position", position,
position + displacement * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
$Tween.start()
Or you can using String thought out, and querying the dictionary every time. Which, I believe, is what you intended. Like this:
extends Area2D
const tile_size:float = 16
export var speed:float = 5
var inputs = { "ui_right": Vector2.RIGHT,
"ui_left": Vector2.LEFT,
"ui_up": Vector2.UP,
"ui_down": Vector2.DOWN }
func _ready():
position = position.snapped(Vector2.ONE * tile_size/2)
func _unhandled_input(event:InputEvent) -> void:
if $Tween.is_active():
return
for dir in inputs.keys():
if event.is_action_pressed(dir):
move(dir)
func move(dir:String) -> void:
$RayCast2D.cast_to = input[dir] * tile_size
$RayCast2D.force_raycast_update()
if !$RayCast2D.is_colliding():
move_tween(dir)
func move_tween(dir:String) -> void:
$Tween.interpolate_property(self, "position", position,
position + input[dir] * tile_size, 1.0/speed, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
$Tween.start()
Notice here that _unhandled_input is passing dir to move. The same way that move is passing dir to move_tween.

error with instancing a ball in my pong game in godot

Hello I am working on a pong game in godot. Everything is going well so far but I recieve this error in my debugger:
E 0:01:55.112 body_set_shape_as_one_way_collision: Can't change this state while flushing queries. Use call_deferred() or set_deferred() to change monitoring state instead.
<C++ Error> Condition "body->get_space() && flushing_queries" is true.
<C++ Source> servers/physics_2d/physics_2d_server_sw.cpp:739 # body_set_shape_as_one_way_collision()
AddBall.gd:18 # _on_Area2D_body_entered()
Here is my code for instancing a duplicate ball:
extends Node2D
var collect = false
var ballscene = null
func _ready():
$Area2D/AnimatedSprite.play("Spin")
func _on_Area2D_body_entered(body):
#print(body.name)
if body.name=="Ball"&&collect==false:
collect = true
$Collection.play()
$AnimationPlayer.play("Fade")
$Area2D/AnimatedSprite.stop()
var ball = load("res://Scenes/BallDuplicate.tscn")
ballscene = ball.instance()
find_parent("SpawnManager").get_node("BallDuplicate").add_child(ballscene)
queue_free()
Yes, I instance the ball in the power up and not my spawn manager.
As the error message recommends, you should postpone your node tree changes with call_deffered() as you are not allowed to kick in with adding another rigid bodies to the scene during collision processing.
extends Node2D
var collect = false
var ballscene = null
func CollectFunc():
if not collect:
collect = true
$Collection.play()
$AnimationPlayer.play("Fade")
$Area2D/AnimatedSprite.stop()
var ball = load("res://Scenes/BallDuplicate.tscn")
ballscene = ball.instance()
find_parent("SpawnManager").get_node("BallDuplicate").add_child(ballscene)
queue_free()
func _ready():
$Area2D/AnimatedSprite.play("Spin")
func _on_Area2D_body_entered(body):
#print(body.name)
if body.name=="Ball":
call_deffered("CollectFunc")
Another alternative is to setup your collisions-related signal so that it calls its callback method in "deferred mode".
In the UI, check the "Deferred" box:
Or in GDScript, connect the signal with the "CONNECT_DEFERRED" flag:
$Area2D.connect("body_entered", self, "_on_Area2D_body_entered", [], CONNECT_DEFERRED)

Unity Input Touch issue

Could you please advise how i would go about using the input touch function in Unity to make an object changes its x direction every time the user tap on the screen. For example, for 2d setting game, an object is moving forward (to the right) in the x position, if the user tap then the object would move backward in the x position (to the left). Sorry no code is produced.
It's simple as your name "tony" :)
What you can do is to make a simple script which'd move your object to left and right. And on screen touch you can easily change its direction by just -1 multiplication.
Simple script that you can attach to your object.
using UnityEngine;
using System.Collections;
public class MoveObject : MonoBehaviour
{
float _limit = 5;
// 1 for right and -1 for left.
float _direction = 1;
// You can call it as speed
float _speed = 0.01f;
void Start ()
{
}
void Update ()
{
transform.position = Vector3.MoveTowards (transform.position, new Vector3 (transform.position.x + _direction, transform.position.y, transform.position.z), _speed);
if (Input.GetMouseButtonDown (0))
_direction *= -1;
}
}
Hope this helps :)

Resizing JavaFX Windows while maintaining aspect ratio?

I have a window, which I have to keep square. My code is
primaryStage.minHeightProperty().bind(scene.widthProperty());
primaryStage.minWidthProperty().bind(scene.heightProperty());
It does resize the square when extending but has problems when I try to make it smaller?
Sometimes one of the sides gets a little shorter or longer than the other one. Is there a fix for this? Did I do something wrong in the code I currently use?
This doesn't answer your question directly, but I think is actually better UX since you don't restrict the user. Instead of fixing the aspect ratio of the stage, consider using insets to pad the content you want to remain square:
private val DEF_PAD = 6.0
chart.paddingProperty.bind(boundValue(stage.widthProperty, stage.heightProperty) {
// Maintain a square plot with padding
val w = stage.getWidth
val h = stage.getHeight
val extra = DEF_PAD + 0.5 * Math.abs(w - h)
if (w > h) {
new Insets(DEF_PAD, extra, DEF_PAD, extra)
} else if (h > w) {
new Insets(extra, DEF_PAD, extra, DEF_PAD)
} else {
new Insets(DEF_PAD)
}
})
(Written in Scala. boundValue is just a utility that creates a binding with a varargs dependency list)

Unity: camera falls through terrain

Problem is:
I have created terrain and I need to fly over terrain with Camera. I added to Camera "Mouse Look" script, RigidBody: usegravity - unchecked and I have added my code in Update method:
float vert = Input.GetAxis("Vertical");
float hor = Input.GetAxis("Horizontal");
if (vert != 0)
{
if (!Physics.Raycast(this.transform.position, this.transform.forward, 5))
{
transform.Translate(Vector3.forward * flySpeed * vert);
}
else
{
transform.Translate(Vector3.up * flySpeed * vert);
}
}
if (hor != 0)
{
if (!Physics.Raycast(this.transform.position, this.transform.forward, 5))
{
transform.Translate(Vector3.right * flySpeed * hor);
}
else
{
transform.Translate(Vector3.up * flySpeed* hor);
}
}
if (Input.GetKey(KeyCode.E))
{
transform.Translate(Vector3.up * flySpeed);
}
else if (Input.GetKey(KeyCode.Q))
{
Vector3 v = Vector3.down * flySpeed;
if (!Physics.Raycast(this.transform.position, this.transform.forward, 5))
{
transform.Translate(v);
}
}
But sometimes then i go down - Q - camera goes through terrain. Why?
Also looks ugly if you are moving with camera forward as low as possible over terrain and camera does not fall through it - it starts to jump. Also why?
Make sure you have a Terrain Collider on your terrain.
In addition to S.Richmonds answer, you can add a character controller or other similar collider-component object to your camera.
See this answer in the unity questions network:
http://answers.unity3d.com/questions/45763/getting-camera-to-not-see-under-the-ground.html
The Update() method in a monobehavior gets called once each fram. Because the rate which update is called is dependent on frame rate, moving an object by a constant value in Update() can result in inconsistant motion. This can be corrected by multiplying a constant speed by Time.deltaTime, which is the time in seconds since the last frame was rendered. This will fix the fallthrough unless flySpeed is set too high (where the change in position each frame is greater than the collider's size). Additionally as suggested above, using a CharacterController without a rigidbody would be better suited to this situation. Rigidbodies are for objects primarily controlled by physics, while the CharacterController is for objects controlled by scripts.