How to make panda3d accept controls faster? - python

Hi I am trying to make a game on panda3d v 1.8.1 (python) but the controls seem to be very sloppy. One has to keep the keys pressed for a second or two to make things happen. Is there any way to make panda3d accept controls faster ?
Here's my code of my key handler :
class KeyHandler(DirectObject):
def __init__(self):
self.accept('arrow_left-repeat', self.lookLeft)
self.accept('arrow_right-repeat', self.lookRight)
self.accept('arrow_up-repeat', self.lookUp)
self.accept('arrow_down-repeat', self.lookDown)
self.accept('w-repeat', self.Moveforward)
self.accept('s-repeat', self.Movebackward)
self.accept('a-repeat', self.Moveleft)
self.accept('d-repeat', self.Moveright)
self.accept('q-repeat', self.MoveDown)
self.accept('e-repeat', self.MoveUp)
self.accept('space', self.Dotask)
def lookLeft(self):
global camxy
camxy += 2
def lookRight(self):
global camxy
camxy -= 2
def lookUp(self):
global camyz
camyz += 2
def lookDown(self):
global camyz
camyz -= 2
def Moveforward(self):
global camx
if camx < 57 :
camx += 1
def Movebackward(self):
global camx
if camx > -32 :
camx -= 1
def Moveleft(self):
global camy
if camy < 42 :
camy += 1
def Moveright(self):
global camy
if camy > -36 :
camy -= 1
def MoveUp(self):
global camz
if camz < 15 :
camz += 0.5
def MoveDown(self):
global camz
if camz >1 :
camz -= 0.5
a = KeyHandler()
def set_cam(task) :
camera.setPos(camx,camy,camz)
camera.setHpr(camxy,camyz,camzx)
taskMgr.add(set_cam, "setcamTask")
The camera which I am using is the default camera of panda3d.
Any help would be appreciated !

You should avoid using the "-repeat" handlers. They take just as long to trigger as more letters take to appear if you hold a key down in any textbox.
The usual way is to use a dict keeping key state:
class KeyHandler(DirectObject):
keys = {"lookLeft": False, "lookRight": False} # etcetera
def __init__(self):
DirectObject.__init__(self)
self.accept('arrow_left', self.pressKey, ["lookLeft"])
self.accept('arrow_left-up', self.releaseKey, ["lookRight"])
taskMgr.add(self.set_cam, "setcamTask")
def pressKey(self, key):
self.keys[key] = True
def releaseKey(self, key):
self.keys[key] = False
# Hopefully method will be passed bound
def set_cam(self, task):
dt = globalClock.getDt()
if self.keys["lookLeft"]:
camera.setH(camera.getH() + 2 * dt)
elif self.keys["lookRight"]:
camera.setH(camera.getH() + 2 * dt)
a = KeyHandler()
This will also allow you to define user settings for keys more easily.
This is not the first or even most important issue with that code though. set_cam should really be a method of KeyHandler instead of declaring every variable global, and you should multiply movement by each frame's dt to keep the game looking the same speed with different framerates.

Related

Why can I not alter the value of shape_amount?

I am struggling to find a solution to add 1 to the value of shape_amount within a different class and return the value back to compare it.
This is the first class that compares the value of shape_amount.
class BEGIN_DRAWING:
def begin_draw(run, shape_opt, color_value, color):
global shape_amount
shape_amount = 0
i = True
while i == True:
color = color_value(color)
run.movement(shape_amount, color)
print(shape_amount)
if shape_amount == shape_opt:
i = False
run.movement(shape_amount, color)
I print shape_amount to check its values but outputs 0
class DRAWING_CURVED:
def fill(shape_amount, color):
rand_int = randint(0, 20)
if rand_int == 0:
end_fill()
fillcolor(color)
begin_fill()
shape_amount += 1
return shape_amount
This is supposed to add 1 to the value of shape_amount every time a shape is filled but it does not seem to be doing so.
From what I can tell, you are passing the shape_amount global variable into a function as a parameter. This parameter will not be treated as the global variable, but as a normal parameter.
The below highlights this, it prints for me:
2
1
2
def test():
global x
x = 1
val = increment(x)
print(val)
print(x)
increment_two()
print(x)
def increment(x):
return x + 1
def increment_two():
global x
x += 1
if __name__ == "__main__":
test()

Need to make sure I interpreted the question correctly (I fixed the question I need some upvotes to lay off my question ban)

Define a class for a type called CounterType. An object of this type is used to count
things, so it records a count that is a nonnegative whole number.
a. Private data member: count.
b. Include a mutator function that sets the counter to a count given as an argument.
c. Include member functions to increase the count by one and to decrease the count
by one.
d. Include a member function that returns the current count value and one that outputs
the count.
e. Include default constructor that set the count to 0.
f. Include one argument constructor that set count to given argument.
Be sure that no member function allows the value of the counter to become negative.
Embed your class definition in a test program.
An output example would be:
a = CounterType(10)
a.display()
a.increase()
a.display()
a.setCounter(100)
a.display
Will display the following:
Counter: 10
Counter: 11
Counter: 100
I have written the code but I just want to make sure that it is following what the question asked and if there could be an easier way to write this code.
class CounterType:
def __init__(self, counter=0):
self.counter = counter
def increase(self):
self.counter += 1
def decrease(self):
if self.counter == 0:
print("Error, counter cannot be negative")
else:
self.counter -= 1
def setCounter(self, x):
if x < 0:
print("Error, counter cannot be negative")
else:
self.counter = x
def setCount0(self):
self.counter = 0
def display(self):
print("Counter:", self.counter)
def getCounter(self):
return self.counter
This is a homework assignment so it would help if you could just give some tips
You forgot "Be sure that no member function allows the value of the counter to become negative."
The naïve way to do this is to add an if condition in every function. A smarter way would be to add that check the setCounter function and use this function from all other functions.
class CounterType:
def __init__(self, counter=0): # (e, f) counter = 0: default argument value so x = CounterType() works and has a counter of 0
self.counter = 0 # (a)
self.setCounter(counter)
def increase(self): # (c)
self.setCounter(self.counter + 1)
def decrease(self): # (c)
self.setCounter(self.counter - 1)
def setCounter(self, x): # (b)
if x < 0:
print("Error, counter cannot be negative")
else:
self.counter = x
def setCount0(self): # This is not needed
self.counter = 0
def display(self): # (d)
print("Counter:", self.counter)
def getCounter(self): # (d)
return self.counter

AttributeError: 'NoneType' object has no attribute 'hero_first'

I keep getting this, have no idea how to make it work, I'm kinda new at python coding and this is giving me a headacke. Could not find any answer at all so please, just tell me what to do
'''
from tkinter import *
import random
import time
# Variables
# Player
class Player(object):
health = random.randint(70,100)
strength = random.randint(70,80)
defence = random.randint(45,55)
speed = random.randint(40,50)
luck = random.randint(40,50)
def __init__(self, arg):
super(Player, self).__init__()
self.arg = arg
# Beast
class Beast(object):
health = random.randint(60,90)
strength = random.randint(60,90)
defence = random.randint(40,60)
speed = random.randint(40,60)
luck = random.randint(25,40)
def __init__(self, arg):
super(Beast, self).__init__()
self.arg = arg
def begin():
print("\nHero"," Beast"
"\nHealth:", Player.health, " Health:", Beast.health,
"\nStrength:", Player.strength, " Strength:",Beast.strength,
"\nDefence:", Player.defence, " Defence:", Beast.defence,
"\nSpeed:", Player.speed, " Speed:", Beast.speed,
"\nLuck:", Player.luck, " Luck:", Beast.luck)
print("\nBEGIN FIGHT")
def round_stat():
print("Hero"," Beast",
"\nHealth:", Player.health," Health:", Beast.health)
def who_move():
hero_first = 1
if Player.speed > Beast.speed:
hero_first = 1
elif Player.speed < Beast.speed:
hero_first = 0
else:
if Player.luck > Beast.luck:
hero_first = 1
else:
hero_first = 0
begin()
round_stat()
who_move()
if who_move().hero_first == 1:
print("SUPPPPPPP")
'''
who_move() is function, not an object. So you can not access variable inside function.
So the quickest solution is set the hero_first as global:
def who_move():
global hero_first
hero_first = 1
who_move()
if hero_first == 1:
print("SUPPPPPPP")
and then I got:
dubak#dubak-thinkpad:~/projects/$ python3 test.py
SUPPPPPPP
or rewrite the who_move function so it is returning directly value of hero_first. Something like this:
def who_move():
hero_first = None
if 2 > 1:
hero_first = 1
return hero_first
if who_move() == 1:
print("SUPPPPPPP")
You cannot access the internal variables of a function using the . syntax. If you want that data to be accessible outside of the function, return it:
def who_move():
if Player.speed > Beast.speed:
return 1
elif Player.speed < Beast.speed:
return 0
else:
if Player.luck > Beast.luck:
return 1
else:
return 0
Then, use it like:
if who_move() == 1
The other answer mentions global, but I would not use that here. Use of global should be limited, as it has a general tendency of making your code harder to maintain and understand.

Python best practices for functions

Approach 1 (global var):
id_constant = 1000
id_cnt = 1
def give_id():
global id_cnt
id_cnt += 1
return id_constant * id_cnt
id = give_id()
Approach 2 (fuc var instead of global var):
id_cnt = 1
def give_id():
id_constant = 1000
global id_cnt
id_cnt += 1
return id_constant * id_cnt
id = give_id()
Approach 3 (pass in global vars):
id_cnt = 1
id_constant = 1000
def give_id(constant, cnt):
return constant * cnt
global id_cnt
id_cnt +=1
id = give_id(id_constant, id_cnt)
im not sure if there are any general rule of thumb but is is widely accepted for a function to access a global variable inside a function? or if the variable is only used for a function, then should it be part of a function variable instead?
The method often depends a little on the situation.
You seem to need unique ids, why not use a generator:
def create_id_generator():
"""Returns an id generator."""
i = 0
while True:
yield i
i += 1
Used with the next() function:
>>> ID_GENERATOR = create_id_generator() # Global variable
>>> my_id = next(ID_GENERATOR)
>>> my_id2 = next(ID_GENERATOR)
>>> my_id3 = next(ID_GENERATOR)
>>> print(my_id, my_id2, my_id3, next(ID_GENERATOR))
0 1 2 3
If you want the ids to be multiples of 1000, you can pass the constant to the generator via parameters:
def create_id_generator(multiplier=1000):
"""Returns an id generator."""
i = 0
while True:
yield i * multiplier
i += 1
You can even add a starting value if you don't want to start from index 0:
def create_id_generator(multiplier=1000, start_index=0):
"""Returns an id generator."""
while True:
yield start_index * multiplier
start_index += 1
If id_constant is actually constant, I would have done:
ID_CONSTANT = 1000
def give_id(id_count):
return ID_CONSTANT * id_count
id_count = 1
id = give_id(id_count)
But it looks like you also have some state (id_count) that needs to be kept up-to-date with the issuing of id, suggesting a generator function:
def give_id(id_count):
while True:
yield ID_CONSTANT * id_count
id_count += 1
or even a class:
class IdCreator(object):
ID_CONSTANT = 1000
def __init__(self, start_count=1):
self.id_count = start_count
def give_id(self):
new_id = self.ID_CONSTANT * self.id_count
self.id_count += 1
return new_id
You could go further and implement iteration for the class.
Global variable is generally something you should avoid.
If you want to have constants, for let's say, configuration purposes I would take more a module approach like:
conf.py
MYCONST = 1000
app.py
import conf
print conf.MYCONST
Or take an OO approach such as:
class Test(object):
def __init__(self):
self._constant = 1000
def give_id(self, cnt):
return self._constant * cnt
From the Zen of Python (i.e. import this)
Namespaces are one honking great idea -- let's do more of those!
In general, if you don't need to put something in the global namespace, it is better to encapsulate it in the local namespace of the function, so I would consider option 2 to be more "pythonic" unless id_constant is going to be used by multiple functions.
You might also try the following using a keyword argument with a default value:
id_cnt = 1
def give_id(id_constant=1000):
global id_cnt
id_cnt += 1
return id_constant * id_cnt
id = give_id()
Then if you ever needed id_constant to be something different, you could call the function as id = give_id(id_constant=500).
A little bit of tricky stuff:
def get_id_func(constant):
class c(object):
def __init__(self, constant):
self.constant = constant
self.id = 0
def func(self):
self.id += 1
return self.id * self.constant
o = c(constant)
return o.func
# create function
f = get_id_func(1000)
# call and test it
assert f() == 1000
assert f() == 2000
assert f() == 3000
Probably you need generator function?
def give_id(id_constant):
delta = 0
while True:
delta += 1
yield id_constant + delta
for i in range(100):
print(give_id(1000)) # prints numbers from 1001 to 1100

Does instantiating a class redefine the class including all of its methods?

I'm making a program that will go through at least 1,016,064 gear permutations on Diablo 3. I've been experimenting with different theoretical implementations and I decided that I wanted to use classes to represent each permutation rather than having to deal with massive and convoluted dictionaries. With this method I can store an instance and then replace it when a new permutation is superior to the former.
In any case it takes my computer (i7-3632QM) about 40 seconds go through all of the permutations just doing about 30 flops per permutation, and I cant even imagine how long it'll take if it has to define all 50 methods each time a class is instantiated. Anyway, this is what I think it'll look like:
class perm:
def __init__(self, *args):
self.x = 10
self.y = 5
self.z = 100
for item in args:
if hasattr(self, item):
getattr(self, item)()
self.val_1 = self.x * 2
self.val_2 = self.y * 5
self.val_3 = self.z/(self.z+300)
def head_1(self):
self.x += 5
self.z + 200
def head_2(self):
self.x += 10
self.y += 10
def feet_1(self):
self.y += 5
self.z += 250
def feet_2(self):
self.x += 10
self.z += 500
current_best = perm('head_1','feet_2')
It seems like the correct way to do this is to make objects for each of the gear options you have, then a function that calculates them all.
import itertools
class Gear(object):
def __init__(self, *args, **kwargs):
# I have no idea what Gear should do...
class Headpiece(Gear):
...
class Legs(Gear):
...
# etc
def calculate_perm(gear_tuple):
result = do_some_calculation_over(gear_tuple)
return result
best = max(itertools.permutations(all_your_gear), key=calculate_perm)
You could even create one class that's analogous to your perm, though I'd give it a more descriptive name:
class EquipmentSet(object):
slots = ['head', 'legs', ... ]
def __init__(self, head=None, legs=None, ...)
self.head = head
self.legs = legs
...
self.equipment = [getattr(self, item) for item in self.slots]
#property
def x(self)
return sum(item.x for item in self.equipment)
# similar for y and z
#property
def val_1(self):
return self.x * 2
# similar for val_2, val_3
# implement dunder rich comparison methods?
result = max(EquipmentSet(*gearset) for \
gearset in itertools.permutations(all_your_gear))
Strings are just as a example. These lists should contain Gear class, which instances knows what type of 'bonuses' gear gives.
import itertools
headpieces = ['headpiece1', 'headpiece2', 'headpiece3']
armors = ['armor1', 'armor2']
weapons = ['weapon1', 'weapon2']
print list(itertools.product(headpieces, armors, weapons))
# result:
[('headpiece1', 'armor1', 'weapon1'),
('headpiece1', 'armor1', 'weapon2'),
('headpiece1', 'armor2', 'weapon1'),
('headpiece1', 'armor2', 'weapon2'),
('headpiece2', 'armor1', 'weapon1'),
('headpiece2', 'armor1', 'weapon2'),
('headpiece2', 'armor2', 'weapon1'),
('headpiece2', 'armor2', 'weapon2'),
('headpiece3', 'armor1', 'weapon1'),
('headpiece3', 'armor1', 'weapon2'),
('headpiece3', 'armor2', 'weapon1'),
('headpiece3', 'armor2', 'weapon2')]
This code gives you all possible gears in lazy way (without passing it to list() it returns generator), is optimized (itertools are implemented in C) as is elegant. Note that in each element there is only one headpiece / weapon / armor. May be generalized to additional piece of gears.
After that you'll just have to write some kind of aggregator which takes input gear and returns 'score'.
Well I decided to use itertools, a module I have no experience with (but that will change after this!), and I've already half made the script making a test. It works so I might as well finish it even if it isn't the most efficient way, although I'm open to suggestions...
import time, itertools
class Barb:
def __init__(_, args):
_.elements = ['arcane','cold','fire','lightning','poison','physical']
_.strength = 5460 # max ancient str
_.vitality = 140
_.armor = 10188
_.all_res = 250
_.resistances = {element:7.7 for element in _.elements}
_.dodge = 0
_.armor_bonus_percent = .25
_.all_res_bonus_percent = 0
_.life_bonus_percent = .25
_.elemental_damage_reduction = 1
_.regen = 10730
_.life_on_hit = 8035
_.life_per_fury_spent = 0
_.life_percent_per_second_regen = 0
_.damage_mod = 1
_.cc = .05
_.cd = 2.8
_.ias = .25
_.attacks_per_second = 1.69
_.ww_damage_percent = 0
_.dibs = 0
_.cdr = 1
_.elemental_damage_bonus = .2
_.bastions = False
# apply gear bonuses
for arg in args:
getattr(_, arg)()
def helm_1(_):
_.cc += .06
_.ww_damage_percent += .15
_.life_bonus_percent += .23
_.resistances['arcane'] += 210
def helm_2(_):
_.cc += .06
_.vitality += 1000
_.life_bonus_percent += .23
_.resistances['arcane'] += 210
def torso_1(_):
_.vitality += 650
_.life_bonus_percent += .15
_.resistances['fire'] += 210
def torso_2(_):
_.all_res += 120
_.vitality += 650
def pants_1(_):
_.vitality += 650
_.armor += 700
_.resistances['physical'] += 210
def pants_2(_):
_.vitality += 650
_.all_res += 120
def bastions_1(_):#ring set
_.strength += 1000
_.cc += .12
_.cd += 1
_.resistances['physical'] += 210
_.resistances['poison'] += 210
_.bastions = True
def bastions_2(_):
_.strength += 500
_.cc += .12
_.cd += 1
_.cdr *= .92
_.resistances['physical'] += 210
_.resistances['poison'] += 210
_.bastions = True
def bk_1(_): # (str, dmg, cdr) + (str, cdr, vit)
_.strength += 2000
_.damage_mod *= 1.05
_.cdr *= .9 * .9
_.vitality += 1000
def bk_2(_): # (str, dmg, cdr) + (str, dmg, loh)
_.strength += 2000
_.damage_mod *= 1.1
_.cdr *= .9
_.life_on_hit += 18000
def best_score():
def available_items(prefix):
#automagically check barb for possible item variants of the item slot 'prefix'
# so more can be added at a later time
r = []
i = 1
while True:
name = '%s_%s'%(prefix, i)
if hasattr(Barb, name):
r.append(name)
else: return r
i += 1
gear_slots = [
'helm','torso','pants','bastions','bk']
helms, torso, pants, bastions, bk = [available_items(i) for i in gear_slots]
gears = itertools.product(helms, torso, pants, bastions, bk)
bestOffense = {'gear':[],
'health':0,
'mitigation':0,
'damage':0}
elapsed = time.time()
while True:
try:
args = next(gears)
barb = Barb(args)
armor = barb.armor * (1 + barb.armor_bonus_percent)
damage_reduction = armor / (armor + 3500)
resistances = {res:(barb.resistances[res] + barb.all_res) \
* (1 + barb.all_res_bonus_percent) for \
res in barb.resistances}
elemental_dr = {res:resistances[res]/(resistances[res] + 350) \
for res in resistances}
health = barb.vitality * 100 * (1 + barb.life_bonus_percent)
aps = barb.attacks_per_second * (1 + barb.ias)
damage_mod = barb.damage_mod * (1 + (barb.strength / 100))
damage_mod *= (1 - barb.cc) + (barb.cc * barb.cd)
damage_mod *= 2.25 if barb.bastions else 1
damage_mod *= 1 + barb.elemental_damage_bonus
dust_devils = 25 * damage_mod * (1 + barb.dibs + barb.ww_damage_percent)
min_elemental_dr = elemental_dr[min(elemental_dr)]
mitigation = 1 - ((1-damage_reduction) * (1-min_elemental_dr))
if dust_devils > bestOffense['damage']:
bestOffense = {'gear':args,
'health':health,
'mitigation':mitigation,
'damage':dust_devils}
except: return bestOffense, time.time() - elapsed
Python static methods will stop the interpreter making a new function in memory for every instance of a class. You can only use it for functions that don't need an instance to operate on though, i.e. functions that don't use self.

Categories