Changing Player's variable depending on current collision - godot

I am trying to modify the player's movement speed depending on if they are in a bush or not. This is the gist of what I am trying to accomplish:
const Grass = preload("res://World/Grass/Grass.tscn")
onready var grass = Grass.instance()
func move():
grass = Grass.instance()
if grass.player_in_grass():
velocity = move_and_slide(velocity / BUSH_FRICTION_MULTIPLIER)
else:
velocity = move_and_slide(velocity)
The issue that I am having is that I cannot figure out what the code to check should be. I have tried to create a player detection zone for the grass, switching its value when inside of it:
var player = null
func player_is_visible():
return player != null
func _on_PlayerDetectionZone_body_entered(body):
player = body
func _on_PlayerDetectionZone_body_exited(body):
player = null
And my Grass.gd looks like this:
onready var playerDetectionZone = $PlayerDetectionZone
func player_in_grass():
if playerDetectionZone.player != null:
return true
else:
return false
After all of this, I am hit with the error:
Invalid get index 'player' (on base: 'Nil').
The error forwards me to 'if playerDetectionZone.player != null:'.
What am I doing wrong? Should I be checking for this/doing this a different, easier way that you know of? All feedback appreciated. Thank you.

To sum it up:
The error is
Invalid get index 'player' (on base: 'Nil').
And the line of code where this error occurs is:
if playerDetectionZone.player != null:
(which is unfortunately not part of the posted code).
This error means that the variable playerDetectionZone has the value null. Assuming the rest of the code is as posted the problem should be located here:
onready var playerDetectionZone = $PlayerDetectionZone
Something is wrong with the node path ($PlayerDetectionZone). Spelling, capitalisation, maybe wrong position in the tree. Could be a number of things.
Edit: Based on your comment it is probably the wrong position in the tree. The line above only works if the PlayerDecetionZone node is a child of the grass node (the one with grass.gd attached).

Related

Godot Invalid to get index 'x' (On Base: 'Node (a.gd)')

im trying to hold a path to a game object (bat.tscn) in a script and use it to instanceate a object from a different script. Name of the script that holds the variable is called a.gd and the one that is responsible for instantiating is b.gd and the name of the variable is x. But for some reason every time i try to use the variable it gives me an error;
Invalid to get index 'x' (On Base: 'Node (a.gd)')
a.gd;
func _process(delta):
var SlotOne = $"res://Objects/Weapons/Bat.tscn
b.gd;
onready var InvManager = get_node("../a") #gets the script.
func _physics_process(delta):
changeWeapons()
func changeWeapons():
if Input.is_key_pressed(KEY_1):
load(InvManager.SlotOne)
elif Input.is_key_pressed(KEY_2):
print("2")
elif Input.is_key_pressed(KEY_3):
print("3")
elif Input.is_key_pressed(KEY_4):
print("4")
any ideas how can i fix this issue?
Im pretty new to the game engine so im kinda stuck here.
It appears you declared SlotOne inside of _process. That makes it a local variable. In other words, this variable will only be availabe inside that _process, and thus, you cannot reach it from another script.
Define the variables you want to reach from other scripts outside of any method (func) - the language guidlines encourage to place them near the start of the file, before any methods.
also a possible explanation is that instead of using or instancing the node you are trying to access it
so first try to add the node to the main scene like this
get_tree().current_scene().add_child("#the path to a.gd")
but anyways only the code can can tell us what is wrong
Well so it looks like you are just loading the scene instead of instancing it
this might help
first make sure that a.gd is a autoload script
then instead of writing the a.gd like this
func _process(delta):
var SlotOne = $"res://Objects/Weapons/Bat.tscn
write it like this
extends node
const SlotOne= preload("res://Objects/Weapons/Bat.tscn")
then write this on b.gd
func _physics_process(delta):
changeWeapons()
func changeWeapons():
if Input.is_key_pressed(KEY_1):
var Bat_instance = A.SlotOne.instance()
Bat_instance.position = Vector_2() #some position
self.add_child(Bat_instance)
elif Input.is_key_pressed(KEY_2):
print("2")
elif Input.is_key_pressed(KEY_3):
print("3")
elif Input.is_key_pressed(KEY_4):
print("4")

What is TileMap:1373

I'm following a tutorial series by Heartbeast.
My grass area2D is being collided with TileMap:1373 in the beginning
extends Node2D
func create_grass_effect():
var GrassEffect = load("res://Effects/GrassEffect.tscn")
var grassEffect = GrassEffect.instance()
var world = get_tree().current_scene
world.add_child(grassEffect)
grassEffect.global_position = global_position
func _on_Hurtbox_area_entered(area):
pass
func _on_Hurtbox_body_shape_entered(body_id, body, body_shape, local_shape):
print(body_id, body, body_shape, local_shape)
func _on_Hurtbox_body_shape_entered(body_id, body, body_shape, local_shape):
print(body_id, body, body_shape, local_shape)
console
1373[TileMap:1373]100
1373[TileMap:1373]100
1373[TileMap:1373]110
Despite moving all tile maps out of the way for the two small green grasses:
When I tell the script to queue_free() on _on_Hurtbox_area_entered:
Flipping the code doesn't work because when I have this code:
extends Node2D
func create_grass_effect():
var GrassEffect = load("res://Effects/GrassEffect.tscn")
var grassEffect = GrassEffect.instance()
var world = get_tree().current_scene
grassEffect.global_position = global_position
world.add_child(grassEffect)
func _on_Hurtbox_area_entered(area):
queue_free()
func _on_Hurtbox_body_shape_entered(body_id, body, body_shape, local_shape):
print(body_id, body, body_shape, local_shape)
The grass disapears anyways.
So the problem must be due to the signal or queue_free()
EDITTTTT:
I was looking and I think this solved my problem:
The question isn't entirely clear on what you want to trigger the signal, but I assume you want it to fire when the player enters the area. The docs for area_entered say:
Emitted when another area enters.
The player is a PhysicsBody, not an Area. Use body_entered instead:
Emitted when a physics body enters.
The body argument can either be a PhysicsBody2D or a TileMap instance (while TileMaps are not physics body themselves, they register their tiles with collision shapes as a virtual physics body).
So I guess the signal is wrong then...
The grass is touching itself and causing it self to queue_free()
I solved the problem by putting the grasses apart from each other.

Godot 3.1 telling me that lock() and get_pixel() are nonexistent functions

I'm attempting to generate a height map from a noise texture. As far as I understand, in order to call get_pixel() on an image in this context, the image must first be locked. However, when I attempt to run the program, it exits with the error: Invalid call. Nonexistent function 'lock' in base 'StreamTexture'.
If I attempt to run it without locking the image, I get the error: Invalid call. Nonexistent function 'get_pixel' in base 'StreamTexture'.
I am certain that the instructions that I am following are for the same version of Godot I am running (3.1), so why is the engine telling me that lock() and get_pixel() are nonexistent functions?
My code is here:
extends Spatial
var width
var height
var heightData = {}
var vertices = PoolVector3Array()
var drawMesh = Mesh.new()
func _ready():
var noiseTexture = load("res://noiseTexture.png")
width = noiseTexture.get_width()
height = noiseTexture.get_height()
noiseTexture.lock()
for x in range(0, width):
for y in range(0, height):
heightData[Vector2(x,y)] = noiseTexture.get_pixel(x,y).r
noiseTexture.unlock()
for x in range(0, width-1):
for y in range(0, height-1):
createQuad(x,y)
var surfTool = SurfaceTool.new()
surfTool.begin(Mesh.PRIMITIVE_TRIANGLES)
for i in vertices.size():
surfTool.add_vertex(vertices[i])
surfTool.commit(drawMesh)
$MeshInstance.mesh = drawMesh
func createQuad(x,y):
#First half
vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
vertices.push_back(Vector3(x, heightData[Vector2(x,y+1)], -y-1))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
#Second Half
vertices.push_back(Vector3(x, heightData[Vector2(x,y)], -y))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y+1)], -y-1))
vertices.push_back(Vector3(x+1, heightData[Vector2(x+1,y)], -y))
Any help is greatly appreciated.
EDIT - I have (tried) to implement the changes that were suggested in the comments (yet I still don't know what to do with the color variable) and have attached a screenshot of my resulting code, as well as some comments I have made to try and explain to myself why the process SHOULD be working (I think). It also shows my node structure, which is why I opted to display this as an image. However, when I try to run this, the program crashes with the error displayed.
Check the docs;
StreamTexture does not have the method lock.
I think the class you are looking to use is Image. The Texture class is typically intended for drawing on the screen or applying to a Material
var noiseImage = Image.new()
noiseImage.load("res://noiseTexture.png")
noiseImage.lock() # Lock the image here
var color = noiseImage.get_pixel(10, 10) # Replace with your height map population
PS:
Just to let you know, I had a lot of issues with memory usage here so make sure you test that also (C# has bad garbage collector though).
You might need to dispose of the image, surface tool, and array mesh (If you remove the terrain object) to maintain optimum performance.
I have run into similar issues with generating heightmap terrains.
nathanfranke is correct and his solution will work.
If you for some reason use an ImageTexture you can call get_data() on that to get the underlying Image. Then you call lock() on the Image just like nathan says in his answer.
Take care to check that your coordinates for get_pixel() are correct. You can set a breakpoint by clicking on the very left edge of the line of code.
I mention this because I was very frustrated until I realized that my coordinate calculations were all int which resulted in the sampled pixel always being at <0,0>.
Here is part of my code for sampling an image into the HeightMapShape.map_data for Bullet heightmap collisions:
var map_w = shape.map_width
var map_h = shape.map_depth
var img_w = img.get_width()
var img_h = img.get_height()
img.lock()
for y in range(0, map_h):
py = float(img_h) * (float(y) / float(map_h))
for x in range(0, map_w):
px = float(img_w) * (float(x) / float(map_w))
index = y * img_h + x
pix = img.get_pixel(px, py)
shp.map_data[index] = pix.r * heightmap_height + heightmap_offset

How to create a Button Listener in TKinter

I am trying to make a simon says game and am having trouble collecting the users choice to compare against the computer's pattern. I am using four buttons to select the colors that flash on the screen. The problem I am having is my program continues to chug along even if my user hasn't selected a color yet, despite my best efforts to stop it, what can I do to stop the program? Here is my code...
Sequence[]
PosColors = ["Yellow","Red","Blue","Green"]
PlayerChoice[]
Score = 0
def Game(Sequence, PlayerChoice,Score):
Score += 1
PlayerChoice = []
Number = randint(0,3)
Sequence.append(PosColors[Number])
for i in Sequence:
RunTime = Score * 1000
Gui.after(1000, blink, rect, SquareCanvas , i)
RunTime = (Score + 1) * 1000
Gui.after(2000, blink, rect, SquareCanvas , White)
X = len(Sequence)
Index = 0
Color = " "
while Color != " ":
BlueButton.config(command = partial(setSelection,NeonBlue))
RedButton.config(command = partial(setSelection,NeonRed))
YellowButton.config(command = partial(setSelection,NeonYellow))
GreenButton.config(command = partial(setSelection,NeonGreen))
Color = getSelection()
print(Color)
while Color != " ":
PlayerTurn(Sequence,PlayerChoice,Score,Index)
X -= 1
Index +=1
def setSelection(Color):
if Color == NeonBlue:
return "NeonBlue"
elif Color == NeonRed:
return "NeonRed"
elif Color == NeonGreen:
return "NeonGreen"
elif Color == NeonYellow:
return "NeonYellow"
def getSelection():
return TheColor
def PlayerTurn(Sequence, PlayerChoice,Score,Index):
PlayerChoice.append(Color)
print(PlayerChoice)
Label1.config(text = 'Well done! \nYou advance to the next round!')
I am planning on passing this through a checker and to loop it until there is an error but I need to get past this front loop first... I know my logic works as I created it using just lists and the command line before moving on the the graphical portion I just need to know how to get the program to stop to collect the user guess, especially as the length of the pattern gets larger. Originally I had the command function included later where I build my Buttons but this placement seems to get as close as possible to what I am looking for. Any help is appreciated thank you
You're going to need to rethink your main program logic. Tkinter is designed such that you should never have your own infinite loop. Tkinter already has an infinite loop -- the mainloop() method of the root window. You can't write logic that waits for input from the user. You have to think of the GUI as in a perpetual state of waiting, and you need set up handlers (button callbacks or event bindings) to react to events.
You also need to understand that a button command can't "return" anything. "Can't" is a bit strong since it obviously can, but there's nothing waiting for the result -- no place to return it to. You need to design your button function such that it sets a global or instance variable.

Pygame head-on collisions not detecting

Im really new to Python and recently I've been working on creating a small, space invaders style game in pygame. I've almost reached the end however, I want to make it so if the enemy ships (block) collide with my ship (player), a collision is detected, removing my both ships and displaying a short "Game Over" message.
So far I have the code for detecting when a bullet and an enemy ship collides, I have rewritten this for if my ship and an enemy ship collides but this code only works if I fire no shots, I also have to be moving from side to side for the collision to be detected (head on collisions do nothing) once the collision is detected and both ships disappear I am still able to fire bullets from the position at which the collision was detected. I have no idea why this happens. If anyone could help me out Id definitely appreciate it.
Heres the code in question:
for i in range(15):
block = Block(BLACK)
block.rect.x = random.randrange(screen_width)
block.rect.y = random.randrange(55) # change to 155 collisions fixed
block_list.add(block)
all_sprites_list.add(block)
for i in range(1):
player = Player()
player.rect.y = 480
player_list.add(player)
all_sprites_list.add(player)
...
for player in player_list:
player_hit_list = pygame.sprite.spritecollide(block, player_list, True)
for player in player_hit_list:
gameover.play()
player_list.remove(player)
all_sprites_list.remove(player)
block_list.remove(block)
all_sprites_list.remove(block)
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
explosion.play()
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 10
UPDATE
I have now managed to get the collision to detect properly, however I am still able to fire once the ships have disappeared (due to collision) is there any way I can hide the bullet once the collision has taken place?
Heres my updated code:
for i in range(15):
block = Block(BLACK)
block.rect.x = random.randrange(screen_width)
block.rect.y = random.randrange(55) # change to 155 collisions fixed
block_list.add(block)
all_sprites_list.add(block)
for i in range(1):
player = Player()
player.rect.y = 480
player_list.add(player)
all_sprites_list.add(player)
...
for player in player_list:
block_hit_list = pygame.sprite.spritecollide(player, block_list, True)
for block in block_hit_list:
gameover.play()
player_list.remove(player)
all_sprites_list.remove(player)
block_list.remove(block)
all_sprites_list.remove(block)
for bullet in bullet_list:
block_hit_list = pygame.sprite.spritecollide(bullet, block_list, True)
for block in block_hit_list:
explosion.play()
bullet_list.remove(bullet)
all_sprites_list.remove(bullet)
score += 10
Since you are working with groups, you might want to use this function, for handling collisions between groups:
Would be something like this (I haven't tried your code)
pygame.sprite.groupcollide(bullet_list, block_up_list, True, True, collided = None)
With both arguments True you remove both from the list. When you learn how to use groupcollide, you are going to notice that it's very useful.
Anyways, look for the function description in the pygame documentation and see some examples.
Hope that helps ;)

Resources