Ursina - Make button only clickable when within a certain radius from camera - python

I have created a 3d button in ursina, but suppose we are a kilometer away from the button, we can still press it. Where is the logic in it? I would like to make the button clickable only when in a certain radius from it.
horror_gamemode = Button(parent = scene, model = 'cube', texture = None, color = color.black, highlight_color = color.dark_gray, scale = 1, position = (3, -49, 4), collider = 'mesh')

If I got it right, you want to make a button not clickable when we are over, lets say, 100 meters. For that you can use ursina's distance function to calculate the position between the camera and the button and if it is less that 100 meters, then make it clickable, otherwise unclickable (you can use .disabled = False # or True).
Example:
from ursina import *
from ursina.prefabs import *
app = Ursina()
horror_gamemode = Button(parent = scene, model = 'cube', texture = None, color = color.black, highlight_color = color.dark_gray, scale = 1, position = (0, 0, 0), collider = 'mesh')
def some_random_func():
print("HI")
def update():
if (distance(camera, horror_gamemode) < 50):
button.color = color.green
button.disabled = False
horror_gamemode.on_click = some_random_func
else:
button.color = color.red
print(distance(horror_gamemode, camera))
button.disabled = True
horror_gamemode.on_click = None
EditorCamera()
app.run()

Related

Making a main screen in Ursina

I am currently making a game with Ursina and i've been struggling to find a code to make a Main-Starting-Screen to start the game or show controlls.
Here is my game code:
game = Ursina()
ground = Entity(model = "untitled.blend", texture = "Soul Sand.jpg", scale = 400, collider = "mesh")
player = FirstPersonController(model = "player.blend", position = (-6.39844, 148.284, 62.5471),jump_height = 7, jump_duration = 0.5, speed = 10, texture = "Player Texture.jpg", collider = "mesh")
camera.z = None
health_bar = HealthBar(bar_color=color.lime.tint(-.25), roundness=.5, value=100)
def input(key):
if key == "shift":
player.speed = 25
elif key == "control":
player.speed = 10
house = Entity(model = "House.blend", texture = "Tree Texture.jpg", scale = 2, collider = "mesh", position = (0, 146.3, 15))
tree = Entity(model = "Tree.blend", scale = 15, texture = "Tree Texture.jpg", collider = "mesh", position = (15.5488, 140, 55.0674))
game.run()
You could try just displaying a quad model which is a 2d square
Menuback=Entity(model="quad"scale=(10,10))
and just make
Allother entities to .enabled=False
And the menu to true
Create a button
And onclick menuback.enabled =False
And the rest od entities set on true
Here is the ursina docs if u need to look at some stuff
https://www.ursinaengine.org/api_reference.html

pyqtgraph cuts off tick labels if showGrid is called

I am attempting to show some subplots using pyqtgraph.
I would like to set x and/or y axis limits and a grid on these plots.
Here an example without the grid:
import numpy as np
import pyqtgraph as pg
x = np.linspace(0, 2*np.pi, 1000)
y = np.sin(x)
app = pg.mkQApp()
win = pg.GraphicsLayoutWidget(show = True)
p1 = win.addPlot(row = 0, col = 0)
p1.plot(x = x, y = y, pen = pg.mkPen(color = 'b', width = 1))
p1.setXRange(0, 2*np.pi, padding = 0)
p1.setYRange(-1, 1, padding = 0)
app.exec_()
If I turn on the grid with:
...
p1.showGrid(x = True, y = True)
app.exec_()
then I get:
As you can see in the bottom left corner, x and y first ticks are cutted off and this issue shows up only when I turn on the grid.
How can I show the grid on my plots (and set properly x and y limits) without cutting off axis tick labels?
This issue appears to have been created in attempt to fix another detailed here on github: https://github.com/pyqtgraph/pyqtgraph/issues/732
In the pyqtgraph/AxisItem.py source, if you hash out the below two lines (1162, 1163) the issue is corrected, but artifacts will show on the axis again:
# Draw all text
if self.style['tickFont'] is not None:
p.setFont(self.style['tickFont'])
p.setPen(self.textPen())
# bounding = self.boundingRect().toAlignedRect()
# p.setClipRect(bounding) # Ignore Clip
for rect, flags, text in textSpecs:
p.drawText(rect, int(flags), text)
Another option, as listed on the github issue link, would be to only show axis ticks that fit the bounding box (overwrite in AxisItem.py source):
# Draw all text
if self.style['tickFont'] is not None:
p.setFont(self.style['tickFont'])
p.setPen(self.textPen())
bounding = self.boundingRect()
for rect, flags, text in textSpecs:
# PATCH: only draw text that completely fits
if bounding.contains(rect):
p.drawText(rect, int(flags), text)
My preferred method though has been to set the grid via the 'top' and 'right' axis and disable it from 'left' and 'bottom', where the tick labels reside. Give this a try:
import numpy as np
import pyqtgraph as pg
x = np.linspace(0, 2*np.pi, 1000)
y = np.sin(x)
app = pg.mkQApp()
win = pg.GraphicsLayoutWidget(show = True)
p1 = win.addPlot(row = 0, col = 0)
p1.plot(x = x, y = y, pen = pg.mkPen(color = 'b', width = 1))
p1.setXRange(0, 2*np.pi, padding = 0)
p1.setYRange(-1, 1, padding = 0)
p1.showGrid(x = True, y = True)
p1.getAxis('left').setGrid(False) # Disable left axis grid
p1.getAxis('bottom').setGrid(False) # Dsiable bottom axis grid
for key in ['right', 'top']:
p1.showAxis(key) # Show top/right axis (and grid, since enabled here)
p1.getAxis(key).setStyle(showValues=False) # Hide tick labels on top/right
app.exec_()
Resulting plot: result
On line 581 of pyqtgraph/AxisItem.py source you can see how the tick bounding rectangles are handled differently if 'self.grid' is True or False:
def boundingRect(self):
linkedView = self.linkedView()
if linkedView is None or self.grid is False:
rect = self.mapRectFromParent(self.geometry())
## extend rect if ticks go in negative direction
## also extend to account for text that flows past the edges
tl = self.style['tickLength']
if self.orientation == 'left':
rect = rect.adjusted(0, -15, -min(0, tl), 15)
elif self.orientation == 'right':
rect = rect.adjusted(min(0, tl), -15, 0, 15)
elif self.orientation == 'top':
rect = rect.adjusted(-15, 0, 15, -min(0, tl))
elif self.orientation == 'bottom':
rect = rect.adjusted(-15, min(0, tl), 15, 0)
return rect
else:
return self.mapRectFromParent(self.geometry()) | linkedView.mapRectToItem(self, linkedView.boundingRect())

Replicating Mouse clicks NameError

I'm trying to incorporate mouse events. I got this code from the link ctypes mouse_events
The code there had an error so I changed some part of the code into making it into an integer.
import win32gui, win32api, win32con, ctypes
class Mouse:
"""It simulates the mouse"""
MOUSEEVENTF_MOVE = 0x0001 # mouse move
MOUSEEVENTF_LEFTDOWN = 0x0002 # left button down
MOUSEEVENTF_LEFTUP = 0x0004 # left button up
MOUSEEVENTF_RIGHTDOWN = 0x0008 # right button down
MOUSEEVENTF_RIGHTUP = 0x0010 # right button up
MOUSEEVENTF_MIDDLEDOWN = 0x0020 # middle button down
MOUSEEVENTF_MIDDLEUP = 0x0040 # middle button up
MOUSEEVENTF_WHEEL = 0x0800 # wheel button rolled
MOUSEEVENTF_ABSOLUTE = 0x8000 # absolute move
SM_CXSCREEN = 0
SM_CYSCREEN = 1
def _do_event(self, flags, x_pos, y_pos, data, extra_info):
"""generate a mouse event"""
x_calc = int(65536 * x_pos / ctypes.windll.user32.GetSystemMetrics(self.SM_CXSCREEN) + 1)
y_calc = int(65536 * y_pos / ctypes.windll.user32.GetSystemMetrics(self.SM_CYSCREEN) + 1)
return ctypes.windll.user32.mouse_event(flags, x_calc, y_calc, data, extra_info)
def _get_button_value(self, button_name, button_up=False):
"""convert the name of the button into the corresponding value"""
buttons = 0
if button_name.find("right") >= 0:
buttons = self.MOUSEEVENTF_RIGHTDOWN
if button_name.find("left") >= 0:
buttons = buttons + self.MOUSEEVENTF_LEFTDOWN
# time.sleep(0.1)
# self.MOUSEEVENTF_LEFTUP
if button_name.find("middle") >= 0:
buttons = buttons + self.MOUSEEVENTF_MIDDLEDOWN
if button_up:
buttons = buttons << 1
return buttons
def move_mouse(self, pos):
"""move the mouse to the specified coordinates"""
(x, y) = pos
old_pos = self.get_position()
x = x if (x != -1) else old_pos[0]
y = y if (y != -1) else old_pos[1]
self._do_event(self.MOUSEEVENTF_MOVE + self.MOUSEEVENTF_ABSOLUTE, x, y, 0, 0)
def press_button(self, pos=(-1, -1), button_name="left", button_up=False):
"""push a button of the mouse"""
self.move_mouse(pos)
self._do_event(self.get_button_value(button_name, button_up), 0, 0, 0, 0)
def click(self, pos=(-1, -1), button_name= "left"):
"""Click at the specified placed"""
self.move_mouse(pos)
self._do_event(self._get_button_value(button_name, False)+self._get_button_value(button_name, True), 0, 0, 0, 0)
def double_click (self, pos=(-1, -1), button_name="left"):
"""Double click at the specifed placed"""
for i in xrange(2):
self.click(pos, button_name)
def get_position(self):
"""get mouse position"""
return win32api.GetCursorPos()
With mouse = Mouse(),
The mouse.click((100, 100), "left") works, but mouse.double_click((100,100), "left") doesn't and come out with error with "NameError: name 'xrange' is not defined"
How can I trouble shoot this?
Thank you very much in advance!
Ah Thanks Furas.
"xrange() was in Python 2.7. In Python 3.x you have to use range()"
Just changed xrange() into range() and it works.
Also the reason why I'm not using PyAutoGUI/pynput that some of the mouse function does not properly work with certain games that uses DirectX inputs and is faulty with the mouse as well. Thus, this code should work properly in case if the PyAutoGUI/pynput does not work.

Scrolling text in pygame

How would I go about achieving scrolling text in pygame? As the Text class is derived from Sprite, I thought I would be able to wrap it in the usual way I would a Sprite object. Instead, it simply disappears off the left edge of the screen, never to return (I can't get it to bounce off each side either).
My aim with this program is to reinforce things I've just covered in some educational material. However, scrolling text wasn't one of them (would look cool though).
Thanks,
Dave
Code(specific block is under comment 'add scrolling text at bottom of screen'):
# Simon Says
# Displays sequences of sounds and/or colors which the user must repeat
from livewires import games, color
import random
# initialise screen
games.init(screen_width = 640, screen_height = 480, fps = 50)
class Game(object):
"""A sequence repeat game"""
def __init__(self):
"""Initialize Game object"""
# create key
self.menu_title = games.Text(value = "Key",
size = 25,
color = color.red,
top = 5,
left = games.screen.width - 100,
is_collideable = False)
self.menu_choice0 = games.Text(value = "0 - Quit",
size = 20,
color = color.red,
top = self.menu_title.bottom + 5,
left = games.screen.width - 120,
is_collideable = False)
self.menu_choice1 = games.Text(value = "1 - Yellow",
size = 20,
color = color.red,
top = self.menu_choice0.bottom + 5,
left = games.screen.width - 120,
is_collideable = False)
self.menu_choice2 = games.Text(value = "2 -Thrust sound",
size = 20,
color = color.red,
top = self.menu_choice1.bottom + 5,
left = games.screen.width - 120,
is_collideable = False)
games.screen.add(self.menu_title)
games.screen.add(self.menu_choice0)
games.screen.add(self.menu_choice1)
games.screen.add(self.menu_choice2)
# add scrolling text at bottom of screen
self.instructions = games.Text(value = "Repeat the sequence by entering the "
"corresponding number.",
size = 20,
color = color.green,
x = games.screen.width/2,
bottom = games.screen.height - 2,
dx = - 0.75)
games.screen.add(self.instructions)
# wrap
if self.instructions.left < 0:
self.instructions.left = games.screen.width
def play(self):
"""Play game"""
# load and set background
black_background = games.load_image("blackbackground.jpg", transparent = False)
games.screen.background = black_background
games.screen.mainloop()
def main():
sequence = Game()
sequence.play()
main()
I don't see anything that would handle it all for you in livewires. You could probably use livewires.Object with some custom code. Build your surface like livewires.game.Text does then, move it around. Or even make it tickable and keep track of what part of the text should be shown and blit just that part to the object surface.

Find the color of a rectangle box in Python using graphics.py

I am designing a game using graphics.py in python. I initially got everything setup except, the game consists of clicking on a box which would flip the color of the box. eg: if the color of the box clicked was white, it would turn black. My code works for turning the white boxes to black, but wont convert the black boxes to white. I know my if statement in while loop is wrong. I want to know how you could get the value of the color of the rectangle in graphics.py so I could make a proper if statement.
# _______________________IMPORTS_________________________
from graphics import *
import random
#________________________________________________________
win = None
m_board = []
# Description:
# Wait for the user to enter a valid move via the mouse. If the player selects a position
# outside the valid range or selects an occupied board space, the player is asked again.
# The function returns the move only when it's valid.
# Return value:
# An integer in the range 0 to 99 representing the move
def make_move():
pos = win.getMouse()
x_axis = pos.x // 50
y_axis = pos.y // 50
move = y_axis * 10 + x_axis
return move
# Description:
# Creating the initial board with random black and white boxes
# Return Value:
# None
def draw_board():
global win, m_board
color = ["white", "black"] #Creating list for the random black/white
win = GraphWin("LOGICX", 500, 600)
for y in range(0, 500, 50):
for x in range(0, 500, 50):
board_box = Rectangle(Point(x, y), Point(x + 50, y + 50))
#Setting the boxes with random black/white
board_box.setFill(color[random.randint(0, 1)])
#Adding each box to the empty list
m_board.append(board_box)
#Setting outline color to differentiate individual boxes
board_box.setOutline("grey")
board_box.draw(win)
game_running = True
while game_running:
move = make_move()
if m_board[move] == "black":
m_board[move].setFill("white")
else:
m_board[move].setFill("black")
There are two significant problems with this code. The first is this line:
if m_board[move] == "black":
You know that m_board is a list of Rectangle instances, why would you expect it to be equal to "black"? We can get at the fill color this way:
if m_board[move].config["fill"] == "black":
Another approach would have been to wrap the Rectangle instances with your own object class that keeps track of things you need to query/change. The second problem is this combination:
x_axis = pos.x // 50
y_axis = pos.y // 50
move = y_axis * 10 + x_axis
...
m_board[move].setFill("white")
Although double slash (//) does non-remainder division, since pos.x & pos.y are float, the result is a float and using a float list index, i.e m_board[move], causes an error. The simple fix is to have make_move() call int() on what it returns.
My rework of all the code:
import random
from graphics import *
WINDOW_WIDTH, WINDOW_HEIGHT = 500, 500
COLUMNS, ROWS = 10, 10
TILE_WIDTH, TILE_HEIGHT = WINDOW_WIDTH // COLUMNS, WINDOW_HEIGHT // ROWS
COLORS = ["white", "black"] # Creating list for the random black/white
def make_move():
pos = win.getMouse()
x_axis = pos.x // TILE_WIDTH
y_axis = pos.y // TILE_HEIGHT
return int(y_axis * COLUMNS + x_axis)
def draw_board():
board = []
for y in range(0, WINDOW_HEIGHT, TILE_HEIGHT):
for x in range(0, WINDOW_WIDTH, TILE_WIDTH):
board_box = Rectangle(Point(x, y), Point(x + TILE_WIDTH, y + TILE_HEIGHT))
# Set the boxes with random black/white
board_box.setFill(random.choice(COLORS))
# Set outline color to differentiate individual boxes
board_box.setOutline("grey")
# Add each box to the empty list
board.append(board_box)
board_box.draw(win)
return board
win = GraphWin("LOGICX", WINDOW_WIDTH, WINDOW_HEIGHT)
m_board = draw_board()
game_running = True
while game_running:
move = make_move()
if m_board[move].config["fill"] == "black":
m_board[move].setFill("white")
else:
m_board[move].setFill("black")

Categories

Resources