Using collide_widget so widgets don't overlap, what is the best solution here? - kivy

I have a character that runs around the screen, and an image of a tree. I have collide_widget working so that when the character runs into the tree, it returns True. The problem is that I am trying to stop the character from running over the tree, the tree should be blocking the characters movement. What I have tried to do was stop the characters movement to the left, when running into the tree from the left. When I do this it works and I can then use the up, down or, right key to run away from the tree. However, if I use this code for all directions, it prevents me from being able to walk away from the tree in any direction once the collide_widget(tree) is True. I understand the problem, but I am not sure the solution. Is there a better way to do this? Here is part of the code. Is there maybe away to just stop movement before the collision happens?
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
print self.pos
print self.app.tree.pos
self.source = 'selectionscreen/left.zip'
self.anim_delay=.20
if self.x < (Window.width * .25):
bglayout.x += 4
else: #collide widget code
if self.collide_widget(self.app.tree)
self.x -= 0
else:
self.x -= 6
elif keycode[1] == 'right':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.x > (Window.width * .70):
bglayout.x -= 4
else:
self.x += 6
elif keycode[1] == 'down':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.y < (Window.height * .25):
bglayout.y += 4
else:
self.y -= 6
elif keycode[1] == 'up':
self.source = 'selectionscreen/back.zip'
self.anim_delay=.1
if self.y > (Window.height * .70):
bglayout.y -= 4
else:
self.y += 6
else:
return False
return True
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'left':
self.source = 'selectionscreen/left1.png'
elif keycode[1] == 'right':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'down':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'up':
self.source ='selectionscreen/back2.png'
else:
return False
return True

you can copy x and y first (to previous x and previous y perhaps), move the player, check if collideWidget is true, and if so, assign the old values from before you moved and it will be like nothing happened.

Related

Python Kivy how do I press multiple keys at once?

I am making a simple game in the coding language python in the module Kivy, where a guy moves across and around the screen using "wasd" commands. The problem I have ran into is not being able to press multiple keys at the same time. For example, if the player wants to press "w" and "d" at the same time to go diagonal in the upper right, they can't. It only lets them go one direction at a time. Below is some of my code.
class MainScreen(RelativeLayout):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.guy_size = dp(50)
self.velocity = 3
self.acceleration = 0.5
with self.canvas:
Color(1, 0, 0)
self.guy = Ellipse(pos=(100, 400), size=(self.guy_size, self.guy_size))
self._keyboard = Window.request_keyboard(self.keyboard_closed, self)
self._keyboard.bind(on_key_down=self.on_keyboard_down)
self._keyboard.bind(on_key_up=self.on_keyboard_up)
Clock.schedule_interval(partial(self.move_guy, 'nothing'), 1/60)
def keyboard_closed(self):
self._keyboard.unbind(on_key_down=self.on_keyboard_down)
self._keyboard.unbind(on_key_up=self.on_keyboard_down)
self._keyboard = None
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
global RUNNING
if keycode[1] == 'w':
self.move_guy('w')
if keycode[1] == 's':
self.move_guy('s')
if keycode[1] == 'a':
self.move_guy('a')
if keycode[1] == 'd':
self.move_guy('d')
if keycode[1] == 'w' and keycode[1] == 'd':
self.move_guy('wd')
return True
def on_keyboard_up(self, keyboard, keycode):
pass
def move_guy(self, direction, *largs):
x, y = self.guy.pos
if direction == 'w':
y += 5
if direction == 's':
y -= 5
if direction == 'a':
x -= 5
if direction == 'd':
x += 5
self.guy.pos = (x, y)

How to integrate computing time in AI reaction?

My program is an implementation of Pong where one of the paddles is to be moved by the computer and the other by the user.
The program works fine with errors being made by the AI for realism. However, the movement of my paddle on screen is stuttering and it seems to be skipping one or two frames.
The program is in Lua(+Love2D).
function Paddle:comp_move(dt)
error = math.random(3) == 2 and true or false
start_time = os.time()
diff = 0
if ball:collides(self) == false then
if ball.y > self.y + self.height then -- Ball is below paddle
-- Ball is moving up and difference is more than 20 pixels
if ball.dy < 0 and (ball.y - self.y - self.height) > 20 then
-- move down
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.min(VIRTUAL_HEIGHT - 20, self.y + PADDLE_SPEED*(dt))
end
end
-- Ball is moving down
if ball.dy > 0 then
-- move down
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.min(VIRTUAL_HEIGHT - 20, self.y + PADDLE_SPEED*(dt))
end
end
elseif ball.y + ball.height < self.y then -- Ball is above paddle
-- Ball is moving down
if ball.dy > 0 and (self.y - ball.y - ball.height) > 20 then
-- move up
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.max(0,self.y - PADDLE_SPEED*(dt))
end
elseif ball.dy < 0 then -- Ball is moving up
-- move up
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.max(0,self.y - PADDLE_SPEED*(dt))
end
end
end
end
I am calculating the time taken to compute by the PC but what operation should i do with it normalize my paddle's movement.
From your code it seems the paddle speed is constant; it might be an idea to replace this by a function, so that initially the paddle moves more slowly, then speeds up, and then slows down again as it arrives at the desired location.
This would make it more realistic in terms of movement behaviour, and it might also solve your problem of stuttering.
Your constant PADDLE_SPEED would need to be replaced by some function that takes a step and returns a value (probably between 0.0 and PADDLE_SPEED) at the appropriate time.

Diagonal movement causing tilemap painting flaws

I was able to make a good assembly of what is happening and killing me for weeks, and I really wanted to know if it was my mistake or just the keys pressed too fast, or the rendering delayed? to deal with the problem, because I'm not finding the culprit for doing so.
This happens when for example I go from diagonal down-rigth to left, and right, then I go back down, I make moves like that, fast, it does not always happen, but enough to disrupt the game.
Movement Script:
extends KinematicBody2D
var velocity = 150
var direction = Vector2()
func _ready():
set_fixed_process(true)
func _fixed_process(delta):
#-----------MOVIMENT-------------------------------
direction = Vector2()
#LEFT
if Input.is_action_pressed("left"):
direction += Vector2(-1, 0)
#RIGHT
elif Input.is_action_pressed("right"):
direction += Vector2(1, 0)
#UṔ
if Input.is_action_pressed("up"):
direction += Vector2(0, -1)
#DOWN
elif Input.is_action_pressed("down"):
direction += Vector2(0, 1)
if direction.x != 0 || direction.y != 0:
direction = direction.normalized()*velocity
move(direction*delta)
Paint Script:
extends Control
var map_size = 64
var preScriptUtils = preload("res://scripts/utils_fuctions.gd")
var utils_functions
onready var player = $"../Player"
onready var tile_map = $"../TileMap"
var posTileMap = Vector2()
var posWorld = Vector2()
var prevRect = Rect2()
var newRect = Rect2()
var distance_gen = 2048
var positionMap_rect = 0
var size_rectMap = 0
func _ready():
utils_functions = preScriptUtils.utils_functions.new()
set_fixed_process(true)
posWorld = Vector2(0, 0)
positionMap_rect = ((distance_gen/32)/2)
size_rectMap = distance_gen/32
surround_map(posWorld)
func _fixed_process(delta):
var posPlayer = player.get_position()
posTileMap = tile_map.world_to_map(posPlayer) - Vector2(positionMap_rect, positionMap_rect)
newRect = Rect2(tile_map.map_to_world(posTileMap), Vector2(distance_gen, distance_gen))
if prevRect.position != newRect.position:
load_newMap()
func gen_data(var _posWorld=Vector2(0, 0), var _posTileMap=Vector2(0, 0), var size=Vector2(0, 0)):
var x = 0
var y = 0
var pos_modx = 0
var pos_mody = 0
while x < size.x:
y = 0
pos_modx = utils_functions.mod(_posWorld.x + x, map_size)
#NOISE
var result_noise
while y < size.y:
pos_mody = utils_functions.mod(_posWorld.y + y, map_size)
if (pos_modx >= 0 && pos_mody >= 0) && (pos_modx < map_size && pos_mody < map_size):
tile_map.set_cell(_posTileMap.x + x, _posTileMap.y + y, biomes(result_noise))
y += 1
x += 1
func load_newMap():
var rectIntersection = newRect.clip(prevRect)
var _x = 0
var _y = 0
#LEFT
if player.direction.x < 0:
if int((newRect.size.x - rectIntersection.size.x)/32) == 1:
_x = int(newRect.position.x/32)
_y = int(rectIntersection.position.y/32)
posWorld.x -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(_x, _y), Vector2(1, size_rectMap))
#RIGHT
elif player.direction.x > 0:
if int((newRect.size.x - rectIntersection.size.x)/32) == 1:
_x = int(rectIntersection.end.x/32)
_y = int(rectIntersection.position.y/32)
posWorld.x += 1
Vector2(_x, _y)
gen_data(Vector2(posWorld.x - 1, posWorld.y), Vector2(_x, _y), Vector2(1, size_rectMap))
#UP
if player.direction.y < 0:
if int((newRect.size.y - rectIntersection.size.y)/32) == 1:
_x = int(newRect.position.x/32)
_y = int(newRect.position.y/32)
posWorld.y -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(_x, _y), Vector2(size_rectMap, 1))
#DOWN
elif player.direction.y > 0:
if int((newRect.size.y - rectIntersection.size.y)/32) == 1:
_x = int(rectIntersection.position.x/32)
_y = int(rectIntersection.end.y/32)
posWorld.y += 1
gen_data(Vector2(posWorld.x, posWorld.y - 1), Vector2(_x, _y), Vector2(size_rectMap, 1))
prevRect = newRect
func surround_map(var _posWorld=Vector2(0, 0)):
posTileMap = - Vector2(positionMap_rect, positionMap_rect)
prevRect = Rect2(tile_map.map_to_world(posTileMap), Vector2(distance_gen, distance_gen))
gen_data(_posWorld, Vector2(-32, -32), Vector2(64, 64))
Do not be fussed with the codes, it's very simple what I'm trying to do, I want to move the player while I generate my world, it works perfectly in the 4 directions, but when it involves the diagonals, it does not go as expected. I'm painting like this, I have two rects that I compare to each frame, I take the retagunlo of the interception and calculate where I should paint or erase.
Video of what is happening:
http://www.dailymotion.com/video/x6286px
But there are these flaws not only by pressing too fast, it also happens in slow motion, while being diagonally down-right and then left or left-right and then back to right, moving on the diagonals seems to be the problem, I've tried a lot of shapes, I have almost a notebook all scribble trying to figure this out, I tried with diagonal checks too and I get the same result, today I lost another day, and it does not leave the place, I'm weeks like this, when I think I fixed it, not really.
I'm using godot.
Up 1:
With #Ryan1729 response I realized that I was really thinking the wrong way, but I try to fix my logic by doing something like below and the error continues, I'm confused:
if direction.x != 0 || direction.y != 0:
var vetor_normalized = direction.normalized()
if abs(vetor_normalized.x) < 1 && abs(vetor_normalized.y) < 1:
var vx = Vector2(direction.x, 0)
var vy = Vector2(0, direction.y)
vx = vx.normalized()*velocity
move(vx*delta)
vy = vy.normalized()*velocity
move(vy*delta)
else:
direction = direction.normalized()*velocity
move(direction*delta)
Up2:
Now I have done so, it seems that solved the flaws, but others appeared, which I still do not understand, this issue of 0 confuses me too much.
var oldPosWorld = Vector2(0, 0)
var rect_tileMap = Vector2(-32, -32)
func load_newMap():
var posPlayer = Vector2(int(player.get_position().x/32), int(player.get_position().y/32))
#LEFT
if posPlayer.x < oldPosWorld.x:
pos_tileMap.x -= 1
posWorld.x -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(pos_tileMap.x, pos_tileMap.y), Vector2(1, size_rectMap))
#RIGHT
elif posPlayer.x > oldPosWorld.x:
pos_tileMap.x += 1
posWorld.x += 1
gen_data(Vector2(posWorld.x - 1, posWorld.y), Vector2(pos_tileMap.x + 63, pos_tileMap.y), Vector2(1, size_rectMap))
#UP
if posPlayer.y < oldPosWorld.y:
pos_tileMap.y -= 1
posWorld.y -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(pos_tileMap.x, pos_tileMap.y), Vector2(size_rectMap, 1))
#DOWN
elif posPlayer.y > oldPosWorld.y:
pos_tileMap.y += 1
posWorld.y += 1
gen_data(Vector2(posWorld.x, posWorld.y - 1), Vector2(pos_tileMap.x, pos_tileMap.y + 63), Vector2(size_rectMap, 1))
oldPosWorld = posPlayer
The movements of the right and bottom are right, the problem is those of the left and above that is left over this empty space(ie dealing with the negatives
).
If I put x - 1 on the left to solve the problem, this happens to me:
I solved this by changing:
var posPlayer = Vector2(int(player.get_position().x/32), int(player.get_position().y/32))
by checks like this:
if ((player.get_position().x)/32)
but now I get this by going to the sides and then up:
Up 3:
I'm not going to put my mind to it, so as not to get frustrated if I'm mistaken, but by the tests I did the failure stopped. The code would look like this by assigning int (player.get_position (). X / 32) separately to oldPosWorld.x, this respectively for y as well, since before I was assigning the position to the player only once the postPlayer variable, this caused errors , when you entered the up-down if, as it was still with the previous position of x, doing so updates everything in its right time.
is still so to the left and up:
I do not quite understand why, and frankly not so soon will I know, I'm too tired to look at it. But I'd like to understand not to err. Apart from this it seems to be happening now as expected.
#LEFT
if int(player.get_position().x/32) < oldPosWorld.x:
rect_tileMap.x -= 1
posWorld.x -= 1
gen_data(Vector2(posWorld.x, posWorld.y), Vector2(rect_tileMap.x, rect_tileMap.y), Vector2(1, size_rectMap))
oldPosWorld.x = int(player.get_position().x/32)
I think that the problem is that you want discrete movement (moving up and right moves 1 up and 1 right) but you're normalizing the vector to length 1 in the first code snippet. In the non-diagonal cases this does nothing because the vectors are length 1 already. But in the diagonal cases the vector is length sqrt(2) before normalization, (you can use the Pythagorean theorem to show this.) So your vector is getting shrunk down to something like Vector2(sqrt(2)/2, sqrt(2)/2)), (sqrt(2)/2 is about 0.707,) which causes the painting offset.
Edit: My best guess now is that the problem is that you are calling gen_data twice when the player moves diagonally. I think you want to figure out the x and y offsets then redraw the map.
Summarizing the error was in the update(which in the case would be oldPosWorld), because they were two blocks of ifs, I would enter one after another, and without updating the position, paints the wrong way because it is in a late position:
Video working:
http://www.dailymotion.com/video/x62e6l9

My Lua maze builder that is braiding

I am building a Lua script that generates a maze using a version of the Recursive Backtracker implemented with a stack rather than recursion. Presently the maze is coming out braided and I can't seem to figure out where in my logic this is happening. The function below takes in x and y as a starting point for generating the maze which is a 2d structure (table of tables):
local function buildMazeInternal(x,y,maze)
local stack = {}
local directions = {'North','East','South','West'}
table.insert(stack,{x=x,y=y})
while #stack > 0 do
local index = 1
local nextX = x
local nextY = y
local braid = false
for i = #directions, 2, -1 do -- backwards
local r = calc:Roll(1,i) -- select a random number between 1 and i
directions[i], directions[r] = directions[r], directions[i] -- swap the randomly selected item to position i
end
while index <= #directions and nextX == x and nextY == y do
if directions[index] == 'North' and y > 1 and not maze[y-1][x].Visited then
maze[y][x].North = true
maze[y-1][x].South = true
nextY = y-1
elseif directions[index] == 'East' and x < width and not maze[y][x+1].Visited then
maze[y][x].East = true
maze[y][x+1].West = true
nextX = x+1
elseif directions[index] == 'South' and y < height and not maze[y+1][x].Visited then
maze[y][x].South = true
maze[y+1][x].North = true
nextY = y+1
elseif directions[index] == 'West' and x > 1 and not maze[y][x-1].Visited then
maze[y][x].West = true
maze[y][x-1].East = true
nextX = x-1
else
index = index + 1
end
end
if nextX ~= x or nextY ~= y then
x = nextX
y = nextY
maze[y][x].Visited = true
table.insert(stack,{x=x,y=y})
else
x = stack[#stack].x
y = stack[#stack].y
table.remove(stack)
end
end
end
I know I'm overlooking something but I can't seem to nail it down. Note that the calc:Roll(1,100) method is a .net method in my app used to simulate rolling dice, in this case 1 * 100 sided die, it could be replaced with a call to math.Random(1,100) for use outside of my application.
I see at least one problem. When you go "want to go up", you check whether the "cell which is up" was visited, and if it is, you "skip" going up.
This does not seem to be correct IMHO. If you want to go up, but the cell which is "up" from the current cell was visited but has a "down" exit, you should still be able to go up (instead of skipping because it is visited).
The same applies to the other directions.
That's all I got.
I found the answer after posting on Reddit. I wasn't setting the initial cell to visited allowing it to be passed through twice. The correction was to add maze[y][x].Visited = true immediately before table.insert(stack,{x=x,y=y}).

How to control more than one image position with Kivy

I am making a game with Kivy where you can move a character around the screen. Earlier, I had it working where when you move the character right, the background image would scroll to the left, and same with the right. Recently, what I tried to do was add the background image to a widget along with another image, with the intention that i could control multiple image locations with 'widget.pos', but as soon as I did this, not only does the second image (hero2) not scroll right or left but neither does the background image. I am wondering what I did wrong in my code here.
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.uix.image import Image
from kivy.core.window import Window
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.label import Label
from kivy.uix.behaviors import ButtonBehavior
from kivy.core.audio import SoundLoader
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.gridlayout import GridLayout
from kivy.uix.screenmanager import FallOutTransition
gamelayout = FloatLayout(size=(300, 300))
class Game(Screen):
pass
class IconButton(ButtonBehavior, Image):
pass
class Bg(Image):
def __init__(self, **kwargs):
super(Bg, self).__init__(**kwargs)
self.allow_stretch = True
self.size_hint = (None, None)
self.size = (1440, 1440)
class MoveableImage(Image):
def __init__(self, **kwargs):
super(MoveableImage, self).__init__(**kwargs)
self._keyboard = Window.request_keyboard(None, self)
if not self._keyboard:
return
self._keyboard.bind(on_key_down=self.on_keyboard_down)
self._keyboard.bind(on_key_up=self.on_keyboard_up)
self.size_hint = (.11, .11)
self.y = (Window.height/2.1)
self.app = App.get_running_app()
def on_keyboard_down(self, keyboard, keycode, text, modifiers):
if keycode[1] == 'left':
self.source = 'selectionscreen/left.zip'
self.anim_delay=.20
if self.x < (Window.width * .25):
self.app.test.x += 4
else:
self.x -= 6
elif keycode[1] == 'right':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.x > (Window.width * .70):
self.app.test.x -= 4
else:
self.x += 6
elif keycode[1] == 'down':
self.source ='selectionscreen/right.zip'
self.anim_delay=.20
if self.y < (Window.height * .25):
self.app.test.y += 4
else:
self.y -= 6
elif keycode[1] == 'up':
self.source = 'selectionscreen/back.zip'
self.anim_delay=.1
if self.y > (Window.height * .70):
self.app.test.y -= 4
else:
self.y += 6
else:
return False
return True
def on_keyboard_up(self, keyboard, keycode):
if keycode[1] == 'left':
self.source = 'selectionscreen/left1.png'
elif keycode[1] == 'right':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'down':
self.source ='selectionscreen/right1.png'
elif keycode[1] == 'up':
self.source ='selectionscreen/back2.png'
else:
return False
return True
class gameApp(App):
def build(self):
global sm
sm = ScreenManager()
game = Game(name='game')
sm.add_widget(game)
hero = MoveableImage(source='selectionscreen/jeezyright1.png', pos=(75, 40))
self.hero2 = Image(source='selectionscreen/hero2.zip', pos=(100, 200))
self.background=Bg(source='selectionscreen/background9.png')
self.test=Bg()
self.test.add_widget(self.background)
self.test.add_widget(self.hero2)
gamelayout.add_widget(self.test)
gamelayout.add_widget(hero)
game.add_widget(gamelayout)
return sm
if __name__ == '__main__':
gameApp().run()
Looks like the problem here is that you are using a FLoatLayout. When you move a normal layout (such as a FloatLayout), it does not automatically change the children's co-ordinates. If you want that behavior, try using a RelativeLayout.
http://kivy.org/docs/api-kivy.uix.relativelayout.html
Peace out

Resources