Using a table in Lua to display text isn't working? - text
I'm using Corona SDK, Sublime Text 3, and Lua. My goal in this particular program is to make a visual novel app that changes when the player clicks on the text displayed at the bottom of the screen to progress to the next text entry, therefore continuing the story.
I set up a bunch of code for logo, title screen, and other things, that works perfectly. What I'm currently trying to do within the visual novel scene is to use a table to draw the text from by change the .TEXT property to select a certain value from the table, therefore selecting the text and making that the new text. Basically, something like... (some dummy code below)
novelText = display.newText (insert the parameters for old text and the old text)
--the variable used to call the value in the table
page = 1
dummy table Novel_pages = {
[1] = nil,
[2] = "new text"
}
(insert runtime event here that calls this function)
page = page + 1
novelText.text = Novel_pages[page]
display.newText(novelText)
That was just dummy code, so please don't mind the format. :) I just want to show how I attempted to call these values from the table, and to show what I was doing without having to make people look through all my code.
So everything works fine in Corona SDK simulator, with the text even changing just momentarily -- until one second later, I get a message that reads
"mainl.lua:160: bad argument #1 to 'newText' (string expected, got table)
stack traceback:
[C]: in function 'NewText'
main.lua:160: in function <main.lua: 156>
?: in function <?169>"
Now's the part where I give you all my code! I hope it's not too much, and that I specified the problem enough. I can't see where I made the error in the table, since it should be JUST replacing the .text and not all the other properties? And then displaying with the new text properties and not have to reference the table at all afterwards? Perhaps there's a problem with needing the program to process the .text change before displaying the visual novel text...
Anyway, please help me! I would appreciate knowing what went wrong here, or being proposed an alternative! And thank you so much :)
Here's the code -- everything starts in function sceneVN()! And please excuse my cringy dialogue ingame c: It's a practice project!
local store = require( "plugin.google.iap.v3" )
local composer = require("composer")
local scene = composer.newScene()
display.setStatusBar( display.HiddenStatusBar ) -- Removes status bar
coins = 5 -- variable that defines the number of coins player has in the game.It will be different
--in a stand alone game, but as a small app meant to demonstrate function, it's necessary to start off
--with a small, defined number in the beginning.
local logo = display.newImage("/Images/logo.png", 155, 275) --code for my personal logo, drawn by me.
--Not actually showcased in the video because of time limit.
logo.alpha = 0
local function makeTitleTrue()
logo:removeSelf()
print("menu should be TRUE")
titleScreen()
end
local function fadeOut()
transition.to(logo, {time = 1000, alpha = 0, onComplete = makeTitleTrue})
end
transition.to(logo, {time = 1000, alpha = 1, onComplete = fadeOut}) -- end of logo code
function titleScreen() -- beginning of title code, which is not managed as a separate scene
title = true
titleImg = display.newImage("/Images/vn_bg.png", 155, 275)
--titleWords = display.newImage("/Images/TitleWords.png", 155, 275)
--fix to flow towards the upper right corner.
local flare = display.newImage("/Images/flare2.png", 40, 30)
flare.xScale = .5
flare.yScale = .5
local flare2 = display.newImage("/Images/flare2.png", 400, 70)
flare2.xScale = .6
flare2.yScale = .6
local flare3 = display.newImage("/Images/flare2.png", -30, 100)
flare3.xScale = .4
flare3.yScale = .4
local flare4 = display.newImage("/Images/flare2.png", 100, 400)
flare4.xScale = .4
flare4.yScale = .4
local flare5 = display.newImage("/Images/flare2.png", 400, 400)
flare5.xScale = .3
flare5.yScale = .3
local flare6 = display.newImage("/Images/flare2.png", 250, 200)
flare6.xScale = .3
flare6.yScale = .3
local function moveFlare1()
transition.to(flare, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare1})
end
local function moveFlare2()
transition.to(flare2, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare2})
end
local function moveFlare3()
transition.to(flare3, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare3})
end
local function moveFlare4()
transition.to(flare4, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare4})
end
local function moveFlare5()
transition.to(flare5, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare5})
end
local function moveFlare6()
transition.to(flare6, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare6})
end
transition.to(flare, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare1})
transition.to(flare2, {time=2500, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare2})
transition.to(flare3, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare3})
transition.to(flare4, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare4})
transition.to(flare5, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare5})
transition.to(flare6, {time=2000, x = math.random(-100, 450), y = math.random(-100, 700), onComplete = moveFlare6})
--add options that can when the screen is tapped, tap on an option twice to select
-- start story
-- continue story
-- coin gambling
-- end game
if (title == true) then
Runtime:addEventListener("tap", sceneVN)
end
end
function forceQuit()
function quit()
os.exit()
end
timer.performWithDelay(1000,quit)
end
function sceneVNChapter2()
return
end
function sceneVN() -- the actual visual novel code itself
display.remove(titleImg)
--display.remove(titleWords)
display.remove(flare)
display.remove(flare2)
display.remove(flare3)
display.remove(flare4)
display.remove(flare5)
display.remove(flare6)
title = false
local coinSheetData =
{
width = 32,
height = 32,
numFrames = 8,
}
local coinimageSheet = graphics.newImageSheet( "/Images/spinning_coin.png", coinSheetData )
local sequenceData =
{
name= "spinning_coin",
start = 1,
count = 8,
time = 1000,
loopCount = 0
}
--the properties of the name plate that can be changed ingame by using ".text" property
local nameOptions =
{
text = "Frankenstein",
x = 165,
y = 450,
width = 310,
font = "Charlesworth.ttf",
fontSize = 22,
align = "left"
}
local bg = display.newImage("/Images/bg4.jpg", 155, 275)
textRect = display.newRect(155, 525, 325, 200)
textRect:setFillColor(.02, .02, .02)
textRect.alpha = .6
page = 1
local frames = display.newImage("/Images/windowframes_gold.png", 155, 275)
display.newText(nameOptions)
local VN_pages = {
[1] = nil,
[2] = "\"Then, seeing as this is a simulation of\n a visual novel dating sim, I have no\n choice but to ask you...\"",
[3] = "\"My lady, would you go on a date with me?\nFrankenstein... butler of the fineest noble,\nCadis Etrama di Raizel?\"",
[4] = "duck",
[5] = "duck",
[6] = "duck",
}
local displayNovelText = display.newText("\"I see. So I\'m supposed to pretend I am\na character in a multi-chapter phone\napp that you\'ve been reading...\"", 165, 500, "Goudy Old Style Regular.ttf", 17)
function changePage()
print("dang it")
page = page + 1
displayNovelText.text = VN_pages[page]
display.newText(displayNovelText)
end
textRect:addEventListener("tap", changePage)
if (coins < 10) then
coinsDigits = 2
else
if (coins > 9) and (coins < 100) then
coinsDigits = 3
else
if (coins > 99) and (coins < 1000) then
coinsDigits = 4
else
if (coins > 999) and (coins < 10000) then
coinsDigits = 5
else
if (coins > 9999) and (coins < 100000) then
coinsDigits = 6
end
end
end
end
end
cooin = display.newSprite(coinimageSheet, sequenceData)
cooin.x = 25
cooin.y = 30
cooin:play()
coinText = display.newText("1", 57 + 4 * coinsDigits, 32, "VCR_OSD_MONO_1.001.ttf", 25)
coinText.text = coins
coinTimer = timer.performWithDelay(2000, cooin, 1)
end
function choiceMade( event ) --the scenes where all the choices are made
if (event.action == "clicked") then
local i = event.index
if (i == 1) then
Runtime:removeEventListener()
titleScreen()
else
if (i == 2) then
system.openURL( "https://www.paypal.com/us/home" )
else
if (i == 3) then
return
end
end
end
end
end -- end of choice scenes
function Outofcoins()
--native alert lack of zero coins
local alertMessage = "Uh oh, looks like you've run out of coins! To continue reading the story, would you like to buy or gameble for coins?"
native.showAlert( "Out of coins!", alertMessage, {"Gamble for coins", "Purchase coins", "Exit to Menu"}, choiceMade)
end
if (coins == 0) then -- conditional branch that alerts Outofcoins if no coins left
Outofcoins()
end
function sceneGambleStart()
function earntCoins()
numberEarnt = 0
local coinsGot = display.newImage("/Images/coins_gold.png", 155, 275)
coinsGot.alpha = 0
local function fadeOutCoinsEart()
transition.to(logo, {time = 2000, alpha = 0})
display.remove(coinsGot)
end
local transitionFade = transition.to(logo, {time = 2000, alpha = 1, onComplete = fadeOutCoinsEarnt})
timer.performWithDelay(2000, transitionFade, 1)
coinText.text = coins + numberEarnt
end
local function gamblerIntro()
nameOptions.text = "Gambler"
local bg = display.newImage("/Images/bg4.jpg", 155, 275)
textRect = display.newRect(155, 525, 325, 200)
textRect:setFillColor(.02, .02, .02)
textRect.alpha = .6
local frames = display.newImage("/Images/windowframes_gold.png", 155, 275)
display.newText(nameOptions)
if (gambleVisit == false) then
display.newText("\"Welcome to the coin gambling shop!\nHere's your chance to earn free coins\nwithout having to use the app store!", 165, 500, "Goudy Old Style Regular.ttf", 17)
--display.newText("\"You can play here once a day if you\'ve\nNO coins in your inventory! You are\ngiven three tries at any game each visit.", 165, 500, "Goudy Old Style Regular.ttf", 17)
--display.newText("\"So, then! What games will you play\nin our shop today? \n \n", 165, 500, "Goudy Old Style Regular.ttf", 17)
else
display.newText("\"Welcome back, player! You have\nthree tries left. So, what games\nwill you try your hand at?", 165, 500, "Goudy Old Style Regular.ttf", 17)
end
end
local function sceneDiceRoll()
--local show background graphics
--draw dice on screen, with function ready to shake/transition on screen when accelerometer
--transition hands up and drop dice animation
-- if dice = # chosen then give coins
end
local function sceneCardChoose()
-- a function that defines the mechanics of the card game.
--draw several options on the screen:
--3 cards, earn 5 coins.
--6 cards, earn 15 coins.
--9 cards, earn 30 coins.
--The player needs 5 coins to read another chapter, but by increasing card numbers,
--depending on the player's choice, show the images of the cards (with whatever numbers, always set)
-- on the screen and allow the player to choose a card. Make a shuffling animation.
-- lay all the cards on the screen, now with randomised positions defined by a number.
--the player may choose one. Event listener, if the number defined = card number, the card flips,
--shows its number, and the player wins the coins. defer to earntCoins.
--if the player chooses the wrong card, show him a "WRONG CARD" result, and ask if he would like another
--round or to exit to the main shop.
return
end
local function sceneGuessNumber()
--this game is not created, but is a dummy function that's shown here. It's included in the
--options to show that if this were a real game, that's what it would look like
return
end
end
The error message says all you need:
"mainl.lua:160: bad argument #1 to 'newText' (string expected, got
table)
if you go to line 160 of your code you'll find the following:
display.newText(displayNovelText)
a few lines above you do this:
local displayNovelText = display.newText("sometext")
Refer to the display.newText documentation to find out how to use this function correctly.
https://docs.coronalabs.com/api/library/display/newText.html
You'll see that display.newText() does not return a string, but a text-object.
It also does not take a text-object as input. That's what the error message is telling you.
To access the text of displayNovelText you have to write displayNovelText.text which you can stuff into display.newText() among other options...
Please make sure you always read the documentation of functions you use. You cannot be successful if you don't know what you are dealing with.
Related
too many values to unpack (expected 3) pygame click issue [duplicate]
This question already has answers here: Pygame - Mouse clicks not getting detected (2 answers) Closed 2 years ago. I am currently working on a CS50 project studying the AI required for tic-tac-toe. however I can't get the runner.py file to run, as I get the following error: Traceback (most recent call last): File "/Users/newtracksuit/Downloads/tictactoe/runner.py", line 57, in click, _, _ = pygame.mouse.get_pressed() ValueError: too many values to unpack (expected 3) here is the full code provided, I have yet to write any actual functionality to the tic-tac-toe program, Just need to understand why this is not working yet import pygame import sys import time import tictactoe as ttt pygame.init() size = width, height = 600, 400 # Colors black = (0, 0, 0) white = (255, 255, 255) screen = pygame.display.set_mode(size) mediumFont = pygame.font.Font("OpenSans-Regular.ttf", 28) largeFont = pygame.font.Font("OpenSans-Regular.ttf", 40) moveFont = pygame.font.Font("OpenSans-Regular.ttf", 60) user = None board = ttt.initial_state() ai_turn = False while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() screen.fill(black) # Let user choose a player. if user is None: # Draw title title = largeFont.render("Play Tic-Tac-Toe", True, white) titleRect = title.get_rect() titleRect.center = ((width / 2), 50) screen.blit(title, titleRect) # Draw buttons playXButton = pygame.Rect((width / 8), (height / 2), width / 4, 50) playX = mediumFont.render("Play as X", True, black) playXRect = playX.get_rect() playXRect.center = playXButton.center pygame.draw.rect(screen, white, playXButton) screen.blit(playX, playXRect) playOButton = pygame.Rect(5 * (width / 8), (height / 2), width / 4, 50) playO = mediumFont.render("Play as O", True, black) playORect = playO.get_rect() playORect.center = playOButton.center pygame.draw.rect(screen, white, playOButton) screen.blit(playO, playORect) # Check if button is clicked click, _, _ = pygame.mouse.get_pressed() if click == 1: mouse = pygame.mouse.get_pos() if playXButton.collidepoint(mouse): time.sleep(0.2) user = ttt.X elif playOButton.collidepoint(mouse): time.sleep(0.2) user = ttt.O else: # Draw game board tile_size = 80 tile_origin = (width / 2 - (1.5 * tile_size), height / 2 - (1.5 * tile_size)) tiles = [] for i in range(3): row = [] for j in range(3): rect = pygame.Rect( tile_origin[0] + j * tile_size, tile_origin[1] + i * tile_size, tile_size, tile_size ) pygame.draw.rect(screen, white, rect, 3) if board[i][j] != ttt.EMPTY: move = moveFont.render(board[i][j], True, white) moveRect = move.get_rect() moveRect.center = rect.center screen.blit(move, moveRect) row.append(rect) tiles.append(row) game_over = ttt.terminal(board) player = ttt.player(board) # Show title if game_over: winner = ttt.winner(board) if winner is None: title = f"Game Over: Tie." else: title = f"Game Over: {winner} wins." elif user == player: title = f"Play as {user}" else: title = f"Computer thinking..." title = largeFont.render(title, True, white) titleRect = title.get_rect() titleRect.center = ((width / 2), 30) screen.blit(title, titleRect) # Check for AI move if user != player and not game_over: if ai_turn: time.sleep(0.5) move = ttt.minimax(board) board = ttt.result(board, move) ai_turn = False else: ai_turn = True # Check for a user move click, _, _ = pygame.mouse.get_pressed() if click == 1 and user == player and not game_over: mouse = pygame.mouse.get_pos() for i in range(3): for j in range(3): if (board[i][j] == ttt.EMPTY and tiles[i][j].collidepoint(mouse)): board = ttt.result(board, (i, j)) if game_over: againButton = pygame.Rect(width / 3, height - 65, width / 3, 50) again = mediumFont.render("Play Again", True, black) againRect = again.get_rect() againRect.center = againButton.center pygame.draw.rect(screen, white, againButton) screen.blit(again, againRect) click, _, _ = pygame.mouse.get_pressed() if click == 1: mouse = pygame.mouse.get_pos() if againButton.collidepoint(mouse): time.sleep(0.2) user = None board = ttt.initial_state() ai_turn = False pygame.display.flip()
pygame.mouse.get_pressed() Returns a sequence of booleans representing the state of all the mouse buttons. The number of buttons is either 4 or 6, that depends on the version of pygame. I recommend to get the state of a single button by subscription: buttons = pygame.mouse.get_pressed() if button[0] == 1: # [...]
What is returned when you execute pygame.mouse.get_pressed()? I see (0, 0, 0) which is the three arguments your code expects. You could work out what your code is returning and add the requisite number of underscores, but if you aren't interested in the remaining arguments, you can use * to handle the rest of the returned values, e.g. click, *_ = pygame.mouse.get_pressed()
Attaching a scrollbar to a listbox
I know that there have been some other questions about this, but was hoping to get some help with my current frame configuration as seen below in the code snippet. I have also attached some images, first is with no scrollbar set up. Second is when I uncommented out my scrollbar code. Frame setup: # -- Top Frame -- # self.top = Frame(master, height = 71, bg = self.topColor) self.top.pack(fill = X) self.bottom = Frame(master, height = 650, bg = self.bottomColor) self.bottom.pack(fill = X) Listbox setup: # myscroll = Scrollbar(self.bottom, orient = VERTICAL) Label(self.bottom, text = 'Files Chosen:', bg = self.bottomColor).place(x = 4, y = 110) self.qListBox = Listbox(self.bottom, width = 30, selectmode = SINGLE) # I did have yscrollcommand = myscroll # myscroll.config(command = self.qListBox.yview) # myscroll.pack(side = RIGHT, fill = Y) self.qListBox.place(x = 4, y = 130) Label(self.bottom, text = 'Deployment Queue:', bg = self.bottomColor).place(x = 360, y = 110) self.dListBox = Listbox(self.bottom, width = 30, selectmode = MULTIPLE) self.dListBox.place(x = 360, y = 130)
Figured out how to resolve this. Created three frames that are inside of my master frame as seen below: my_frame = Frame(self.master) my_secondF = Frame(self.master) my_thirdF = Frame(self.master) Once I did this I simply put my Lisboxes inside of those frames and placed them accordingly and configured my scrollbars self.qListBox = Listbox(my_frame, yscrollcommand=myscroll_bar, width = 32, selectmode = SINGLE) I still, however, appreciate all the replies :)
Function traceback says not receiving positional arguments
Issue begins at def displayAnswer import tkinter as tk #icftk stands for incompressible-flow toolkit """This program is being built to aid in getting all parameters of a flow given certain initial conditions""" #CREATES THE WINDOW root = tk.Tk() root.title("Incompressible Fluid Toolkit") class flow: """This class contains all necessary parameters needed to define a flow that i incompressible, 1D-Steady, idiabatic and encounters no energy gain or loss""" def __init__(self,vel,diameter,density,viscosity,massflow = 0, Re = 0, newdia = 1, jetforce = 0, newvel = 0): """initialize a fluid with given basic measured properties""" self.vel = vel self.diameter = diameter self.density = density self.viscosity = viscosity self.massflow = massflow # mass flow rate self.Re = Re #Reynolds Number self.newdia = newdia # downstream diameter for velocity change self.jetforce = jetforce # force the stream can produce normal to a surface self.newvel = newvel # new velocity after a cross sectional area change def reynolds(self): """This function calculates reynolds Pass ro, v, D and mu in the same unit system, in that order""" self.Re = (self.diameter*self.vel*self.density)/(self.viscosity) print(f"The Reynolds number for this flow is {self.Re}") def mdot(self): """This function finds the mass flowrate of a flow""" flowarea = 3.14159*(self.diameter**2) / 4 self.massflow = self.density*self.vel*flowarea print(f"The mass flowrate is {self.massflow}") def streamforce(self): """This function gives the max force that the fluid jet can apply normal to a surface perpendicular to the flow""" self.jetforce = self.massflow*self.vel print(f"The maximum force the jet can apply is {self.jetforce}") def velchange(self): """This function is used to determine the velocity change of a flow when there is a change in cross sectional area of the pipe""" newarea = 3.14159 * (self.newdia**2) / 4 self.newvel = self.massflow/(self.density*newarea) print(f"At the location of the area change, there is a velocity change from {self.vel} to {self.newvel}") #ALL ABOVE FUNCTIONS HAVE BEEN CONFIRMED TO WORK WITH GIVEN TEST CONDITIONS BELOW #use test case velocity = 18.64, diameter = 0.017, density = 1.23, and viscosity = 0.0000184 #Display Entry Boxes velo = tk.Label(root, text="Flow Velocity") # Create a text label velo.grid(row = 0, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element veloent = tk.Entry() veloent.grid(row = 0, column = 1, pady = 10) diam = tk.Label(root, text="Pipe Diameter") # Create a text label diam.grid(row = 1, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element diament = tk.Entry() diament.grid(row = 1, column = 1, pady = 10) dens = tk.Label(root, text="Fluid Density") # Create a text label dens.grid(row = 2, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element densent = tk.Entry() densent.grid(row = 2, column = 1, pady = 10) visc = tk.Label(root, text="Fluid Viscosity") # Create a text label visc.grid(row = 3, column = 0, pady = 10) # Pack it into the window, padding determines how mach space is around a window element viscent = tk.Entry() viscent.grid(row = 3, column = 1, pady = 10) #Display answers at the bottom of the window def displayAnswer(veloent,diament,densent,viscent): ve = float(veloent)#gets velocity entry and turns it into a float di = float(diament)#gets diameter entry and turns it into a float de = float(densent)#gets density entry and turns it into a float vi = float(viscent)#gets viscosity entry and turns it into a float fluid = flow(ve,di,de,vi) fluid.reynolds() fluid.mdot() fluid.streamforce() reynoldsanswer = tk.Label(root, text = "f{fluid.reynolds}") reynoldsanswer.grid(row = 5) mdotanswer = tk.Label(root, text = "f{fluid.mdot}") mdotanswer.grid(row = 6) streamforceanswer = tk.Label(root, text = "f{fluid.streamforce}") streamforceanswer.grid(row = 7) calculatebutton = tk.Button(root,command = displayAnswer) calculatebutton.grid(row = 4) root.mainloop() I am new to tkinter, trying to get experience designing simple GUI. I am using a button to initiate a calculation to obtain values about an incompressible flow. When the button is pressed, the console throws this error. Exception in Tkinter callback Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/tkinter/__init__.py", line 1705, in __call__ return self.func(*args) TypeError: displayAnswer() missing 4 required positional arguments: 'veloent', 'diament', 'densent', and 'viscent' Similarly, if I try to convert the Entry into a float from a string outside of the function, the console throws a cannot convert string to float error. Honestly not sure if all of the code is even right, but I'll cross these bridges one at a time. Any insight is appreciated. Best, T
The problem is that your function requires parameters, but never uses them. When you call the function from a button, the button will not by default pass any options. That's why you get missing 4 required positional arguments - the function requires four, the button passes zero. Since your function is actually doing the right thing and fetching the values it needs, there's no need to pass in the parameters. The simple fix is to simply remove them from the definition of the function: def displayAnswer(): ve = float(veloent)#gets velocity entry and turns it into a float di = float(diament)#gets diameter entry and turns it into a float de = float(densent)#gets density entry and turns it into a float vi = float(viscent)#gets viscosity entry and turns it into a float ...
How to successfully detect when two moving labels touch each other in tkinter?
Here is my current detection code, but it simply doesn't work. def collDetect(xE, yE): global a, b if xE in range(a - 20, a + 20) and yE in range(b - 20, b + 20): uLose = Label(bg = "red", text = "YOU LOSE", font = "comic 48", fg = "black") uLose.place(x = 35, y = 100) mover.place_forget() ball.place_forget() return False xE and yE are the x and y co-ordinates for the enemy label that you are supposed to avoid and a and b are the x and y for the player's label. This function runs every time the movement subroutine for the enemy label loops. I have tried a few different methods for this detection and I am beginning to suspect that it is just a limitation of tkinter, although I am probably wrong. What is wrong here? Is it my code, or is tkinter not quick enough to calculate it in time?
Having an issue getting TKinter to track mouse movement to an object
I was unsure whether to post the full code or not, but here's what I have: from tkinter import * from random import randint HEIGHT = 500 WIDTH = 800 MID_X = WIDTH/2 MID_Y = HEIGHT/2 SHIP_R = 15 SHIP_SPD = 10 bub_id = list() bub_r = list() bub_speed = list() MIN_BUB_R = 10 MAX_BUB_R = 30 MAX_BUB_SPD = 6 GAP = 100 window = Tk() window.title('Bubble Blaster') c = Canvas(window, width=WIDTH, height=HEIGHT, bg='darkblue') c.pack() ship_id = c.create_polygon(5, 5, 5, 25, 30, 15, fill='red') ship_id2 = c.create_oval(0, 0, 30, 30, outline='red') c.move(ship_id, MID_X, MID_Y) c.move(ship_id2, MID_X, MID_Y) def move_ship(event): fixed = True while fixed == True: ship_x, ship_y = event.x, event.y c.move(ship_id, ship_x, ship_y) c.move(ship_id2, ship_x, ship_y) sleep(0.01) def create_bubble(): x = WIDTH + GAP y = randint(0, HEIGHT) r = randint(MIN_BUB_R, MAX_BUB_R) id1 = c.create_oval(x-r, y-r, x+r, y+r, outline='white') bub_id.append(id1) bub_r.append(r) bub_speed.append(randint(1, MAX_BUB_SPD)) def move_bubbles(): for i in range(len(bub_id)): c.move(bub_id[i], -bub_speed[i], 0) def get_coords(id_num): pos = c.coords(id_num) x = (pos[0] + pos[2])/2 y = (pos[1] + pos[3])/2 return x, y def del_bubble(i): del bub_r[i] del bub_speed[i] c.delete(bub_id[i]) del bub_id[i] def clean_up_bubs(): for i in range(len(bub_id)-1, -1, -1): x, y = get_coords(bub_id[i]) if x < -GAP: del_bubble(i) from math import sqrt def distance(id1, id2): x1, y1 = get_coords(id1) x2, y2 = get_coords(id2) return sqrt((x2-x1)**2 + (y2-y1)**2) def collision(): points = 0 for bub in range(len(bub_id)-1, -1, -1): if distance(ship_id2, bub_id[bub]) < (SHIP_R+bub_r[bub]): points += (bub_r[bub] + bub_speed[bub]) del_bubble(bub) return points c.create_text(50, 30, text='TIME', fill='white') c.create_text(150, 30, text='SCORE', fill='white') time_text = c.create_text(50, 50, fill='white') score_text = c.create_text (150, 50, fill='white') def show_score(score): c.itemconfig(score_text, text=str(score)) def show_time(time_left): c.itemconfig(time_text, text=str(time_left)) from time import sleep, time BUB_CHANCE = 20 TIME_LIMIT = 30 BONUS_SCORE = 1000 # MAIN GAME LOOP c.bind("<B1_Motion>", move_ship) score = 0 bonus = 0 end = time() + TIME_LIMIT while time() < end: if randint(1, BUB_CHANCE) == 1: create_bubble() move_bubbles() move_ship("<B1_Motion>") clean_up_bubs() score += collision() if (int(score / BONUS_SCORE)) > bonus: bonus += 1 end += TIME_LIMIT show_score(score) show_time(int(end-time())) window.update() sleep(0.01) c.create_text(MID_X, MID_Y, \ text='PARTY TIME, EXCELLENT', fil='white', font=('Helvetica', 30)) c.create_text(MID_X, MID_Y + 30, \ text='Score: ' + str(score), fill='white') c.create_text(MID_X, MID_Y + 45, \ text='BONU TIME: ' + str(bonus*TIME_LIMIT), fill='white') I'm a complete beginner when it comes to python, and have been given an assignment to only use tkinter and the standard libraries to give mouse movement to this "game". I just can't seem to get the right grasp of it. Any suggestions would be appreciated!
The first step is to remove your while loop, and put most of your functionality into a function. Put everything you want to do in a single frame of animation into this function. Next, call this function on a regular interval using the after command. This allows the event loop to run continuously, which is important for your UI to be responsive. You can do your time() < end calculation inside this function, and use the result to break the cycle once time is up. It looks something like this: def draw_one_frame(): if randint(1, BUB_CHANCE) == 1: create_bubble() move_bubbles() # move_ship("<B1_Motion>") clean_up_bubs() score += collision() if (int(score / BONUS_SCORE)) > bonus: bonus += 1 end += TIME_LIMIT show_score(score) show_time(int(end-time())) if time() < end: after(10, draw_one_frame) You can use the first parameter to after to control how many frames per second you wish your program to run at. Next, you need to handle mouse movement. You do this by creating a binding to the movement of the mouse. You do this outside of the draw_one_frame method. It looks something like this: c.bind("<B1-Motion>", move_ship) You also need to remove the infinite loop from move_ship, and also remove the sleep. You simply need to do all the calculations for the current mouse position. The function will be already be looping -- being called once each time the mouse moves. Finally, you need to call window.mainloop() after all your other code, so that the program can process events. This should be the very last line of your program. It will run until the window is destroyed.