I'm new to gdscript trying make 2D platformer game in godot3,I'm able to instance a fire scene when the Player node enters the area2D which is im using as field of vision but the scene only instantiate once.How do we keep instancing the fire scene till the player is within the field of vision (Area2D)?
My GDscript :
#monster.gd
func _on_field_of_detection_body_entered(body):#signal passed from Area2D
if body.name == "Player": # When field of detection detect the player it load the fire.tscn scene.
var fir = fire.instance()
fir.set_global_position($fireposition.get_global_position())
get_parent().add_child(fir)
Related
I'm working on a game where a level is separated into rooms where the camera is confined into the current room, meaning the camera wont "bleed" into adjacent ones. A room is a scene RoomBase consisting of Positions RoomLimitTopLeft and RoomLimitBottomRight (which define the camera's limits) and an Area2D which detects when the player enters the room. As each room's size and exits will wary, I created a script which streches the Area2D's CollisionShape to the rooms's size.
RoomBase.gd
const ROOM_TRANS_LIMIT = 16
onready var limit_tl = $RoomLimitTopLeft # Position
onready var limit_br = $RoomLimitBottomRight # Position
onready var room_transition = $RoomTransition # Area2D
onready var room_transition_coll = $RoomTransition/CollisionShape2D
func setRoomTransition():
var width = limit_br.position.x - limit_tl.position.x - 2*ROOM_TRANS_LIMIT
var height = limit_br.position.y - limit_tl.position.y - 2*ROOM_TRANS_LIMIT
self.room_transition.position = Vector2(limit_br.position.x/2, limit_br.position.y/2)
self.room_transition_coll.shape.set_extents(Vector2(width/2, height/2))
This code centers the Area2D's position to its room and stretches its collision to cover almost the whole room, which works fine with only one room. However, if a level consists of multiple rooms, the most recent room instance overwrites the collision shape of the previous ones. For example, if Room 1 is horizontal and Room 2 is vertical, then Room 1 will share 2's vertical collision shape.
Is it possible to change an individual scene instances collisions without affecting any others?
you could also change the setting of the node, if possible, in the Inspector
I'm not sure if this is the issue, but for what I understand you are using the same Shape2D for every room. If all they are all using the same Shape2D, and you modify that Shape2D, the changes are reflected in all of them.
You can duplicate the Shape2D from code (or make it unique from the inspector). That way you have a different Shape2D.
i have made a cannon shooting game for two player, the aim is to break the castle down and then try to land the cannon ball onto the opponent but the problem is idk how to write code to show who has won the game when the game is over and all i know is that the code will need to written in the game over section & I am new to code as well
void CMyGame::OnUpdate()
{
if (m_mode == MODE_SHOOT)
{
/////////////////////////////////////////////////
// this code will only be executed while shooting
// TODO: 1. Add the gravitation
m_ball.Accelerate(0, -8);
// TODO: 2. Check if cannon hit? game over?
if (m_ball.HitTest(m_cannons[1 - m_turn]))
{
GameOver();
}
// TODO: 3. Check if the ball is outside the screen. Test the following cases:
// - ball to the left of the screen
if (m_ball.GetX() < 0)
NextTurn();
// - ball to the right of the screen (screen width = 800)
if (m_ball.GetX() > 800)
NextTurn();
// - ball below the screen
if (m_ball.GetY() < 0)
NextTurn();
// TODO: 4. Check if the ball hit the castle?
for each (CSprite * pSprite in m_castle)
{
if (m_ball.HitTest(pSprite))
NextTurn();
}
}
// Check if the castle has taken damage
for (CSprite* pSprite : m_castle)
{
if (m_ball.HitTest(pSprite))
pSprite->Delete();
}
m_castle.delete_if(deleted);
// Update the ball's position
m_ball.Update(GetTime());
the part i need help on is how to write code that will show who has won the game
// called when Game is Over
void CMyGame::OnGameOver()
{
m_cannons[1 - m_turn]->SetImage("fire");
m_mode = MODE_GAMEOVER;
}
Hard to say without seeing the rest of your code and info on how your game works, but you can follow something like this:
Assuming you can check when a cannon lands somewhere and can track the % hit points of each player (which I believe you can do considering you have gravity planned in your given code):
Write a function which is called each time a cannon ball lands, check if the ball has dealt the final blow on the opponent's character. From what you say, you probably do not need to do this check until the castle has been broken down. So something along the lines of if(!castleBroken) {return;} at the start of the function would only check the opponent's HP if the castle is broken (again, assuming that the opponent can only take damage if the castle is broken). Here, castleBroken is a bool which starts as false and becomes true when the cannon has broken the opponent's castle.
This is a similar idea to a Tic-Tac-Toe game I wrote in the past, where after each move, I would check the board state. If the game was over, the game over screen would be printed with the winner declared, or in some cases, a draw.
This is difficult to explain with typing...
I have a GameController scene (Node2D) that holds 3 instanced scenes within:
Mouse (scenes/Mouse.tscn) - this just swaps the mouse cursor for a custom graphic
HeaderBar (scenes/HeaderBar.tscn) - this is the score/label that just sits up top
Messages (scenes/Messages.tscn) - this is the "popup" message box that displays text to the user
In the main scene (Level1.tscn) I instance the GameController scene and it "works" fine. The header bar is there with the score/label and the custom mouse cursor is there and the message box is there (but I have it hidden by default but if I toggle its visibility in the remote it will show up).
Here's where my confusion comes in...
If I attempt, in the GameController script, to manipulate any of those nodes in the GameController scene (the mouse, header, messages) they return as null and will throw an error. For example; if I try to update the score in the $HeaderBar/Control/score I get the following:
Invalid set index 'text' (on base: 'null instance') with value of type 'String'.
The code completion will autofill the node names as I type them (so it recognizes them in the group) but any attempt to reference/use them in script throws similar errors to above.
I'm very new to Godot so I'm sure it's just some misunderstanding I have on how this type of thing works but I'm stumped! Any insight is much appreciated!
UPDATE
I will try to simplify my explanation a bit (I have made some changes). Okay here is the object script:
extends StaticBody2D
onready var main = load("res://scenes/MainGame.gd").new()
func _ready():
pass
# mouse [left] clicked on object
func _on_trigger_input_event(viewport, event, shape_idx):
if Input.is_action_just_pressed("left-click"):
main.display_message("you [left] clicked on the object!")
the call to main.display_message() works. It does call the function but here is that function in the MainGame.gd
extends Node2D
onready var box = $Message/Control
onready var label = $Message/Control/Label
func _ready():
# hide the mouse cursor (will use custom cursor)
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
func display_message(msg):
label.text = msg
box.visible = true
It errors out because label (and box) are null. If I call display_message from the _ready function (in the MainGame.gd) it works as it should. Call it from outside (in the Object.gd) and the nodes are null for some reason.
This instances the scene as expected:
onready var main = load("res://scenes/MainGame.gd").new()
However, this code will run when the instanced scene is added to the scene tree:
onready var box = $Message/Control
onready var label = $Message/Control/Label
func _ready():
# hide the mouse cursor (will use custom cursor)
Input.set_mouse_mode(Input.MOUSE_MODE_HIDDEN)
Yes, both onready variables and _ready will run when this is added to the scene tree.
And when do you add main to the scene tree? I don't see where.
Then when you do this:
main.display_message("you [left] clicked on the object!")
Both box and label will still be null (which is the default value).
Thus, add it to the scene tree, for example:
func _ready():
add_child(main)
Except that does not work either, does it? Look at the code again:
onready var main = load("res://scenes/MainGame.gd").new()
You are instancing a script. A script. Not a scene. It will not have its children nodes and so on. You want to instance a scene, for example:
onready var main = load("res://scenes/MainGame.tscn").instance()
However, I find it odd that you are doing this in a StaticBody2D. I don't think it makes sense that MainGame belongs to a StaticBody2D.
I suspect, you are instancing MainGame in multiple places expecting it be the same instance. But it is not the same instance, it is a new instance.
Here is where I suggest you make MainGame into a autoload, so there is a single instance that you can access from anywhere. However, perhaps that is not right either. Perhaps you should use a Signal Bus. See Why does this variable keep returning to its original value? which had a similar problem (they where trying to open a door, you are trying to show a message, but still).
I have been trying to develop keyboard and touch indexed game, playable on PC browsers and phone. How can I index?
Tried some samples in Godot Sample menu. None of them helped. Either for keyboard or touch screen
func _unhandled_input(event):
if event is InputEventScreenTouch:
if event.pressed:
# Down
if !_os2own.has(event.index): # Defensively discard index if already known
var ptr_id = _find_free_pointer_id()
state[ptr_id] = event.position
_os2own[event.index] = ptr_id
else:
# Up
if _os2own.has(event.index): # Defensively discard index if not known
var ptr_id = _os2own[event.index]
state.erase(ptr_id)
_os2own.erase(event.index)
return true
Need to touching and clicking game. Both for APK and HTML
For simple input handling (e.g. pressed and released) you'll want to map input to actions. You can add actions via "Project Settings -> Input Map" or InputMap singleton.
From the "Project Settings -> Input Map" you can map mouse, keyboard, and controller input to actions.
For touch screens you can use a TouchScreenButton and set it's action. When pressed or released it will send that action event down the scene tree via _input(). TouchScreenButton hides the logic needed to write in _input() to handle presses and releases, such as: is this finger index new? which finger index moved last frame? is this finger area in bounds of input area? and more. While also having the advantage over a plain Button by also emitting an action and can have no texture as it uses a Shape for input detection.
So this creates an one-to-many relationship from actions to inputs like:
my_action -> left mouse button pressed,
-> controller r1 pressed,
-> right half of touch screen pressed,
-> control-shift-f pressed
Using the action
func _input(event):
if not event.is_action('my_action'):
return
if event.is_action_pressed('my_action'):
start_something()
else:
stop_something()
Going further
Since the post specified keyboard and touch input, I only covered press and release input action mapping. However, you can map complex inputs like gestures to actions as well. You can inherit from InputEvent or any of it's subclasses to extend or create a new event. You can map the new event class to an action and then manually process input to find an event. Then you stop the propagation of the event, form the event from your new event class, and then call Input.parse_input_event(my_new_event) to send it down the tree.
Custom Action
# SomeLeafNode.gd
class MyEvent extends InputEvent:
var my_custom_message = 'Hello, World'
func _ready():
InputMap.add_action('my_event')
InputMap.action_add_event('my_event', MyEvent.new())
func _input(event):
# ... logic to see if event could be MyEvent
get_tree().set_input_as_handled()
var my_event = MyEvent.new()
my_event.my_custom_message = 'Caught my event!'
Input.parse_input_event(my_event)
# SomeInputHandlingGameplayNode.gd
func _input(event):
if event.is_action('my_event'):
print(event.my_custom_message) # prints 'Caught my event!'
I'm trying to make a dating sim as a easy first game programming-wise. I don't know how to change the character sprites inside the scripts.
character_sprite.gd
extends Sprite
var char_tex = load("res://Sprites/Lu2.png")
func _ready():
set_texture(char_tex)
func _input(event):
if event is InputEventMouseButton:
char_tex = load("res://Sprites/Lu1.png")
update()
Just set the texture property to the desired texture. You could also preload the textures and then just switch them instead of loading them again.
extends Sprite
var char_tex = load("res://Sprites/Lu2.png")
func _ready():
set_process_input(true)
texture = char_tex
func _input(event):
if event is InputEventMouseButton:
texture = load("res://Sprites/Lu1.png")
The problem in your example was that you only assigned a new image to the char_tex variable, but that doesn't change the texture of the sprite. The texture will still refer to the previous image until you assign the new one with texture = or set_texture. Gdscript is relatively similar to Python in this regard, so I recommend taking a look at Ned Batchelder's talk Facts and myths about Python names and Values.