How to detect collision using PyBox2d and use that information - python

I am trying to filter collisions occurring in my Box2D world by reproducing this example: https://github.com/pybox2d/pybox2d/blob/master/examples/collision_filtering.py
I have four classes in my world, Car, Wheel, Building, and Pedestrian, I want to filter which instance collided with which and one of the possible outputs is (pseudo-code)
if contact.FixtureA.isinstance(Pedestrian) and contact.FixtureB.isinstance(Car):
print("You have caused a traffic accident")
I have this set of categories
CAR_CATEGORY = 2
PEDESTRIAN_CATEGORY = 4
BUILDING_CATEGORY = 8
box2world = world(gravity =(0.0, 0.0), doSleep =True)
I also tried this: but it doesn't work (it does nothing)
class myContactListener(b2ContactListener):
def __init__(self):
b2ContactListener.__init__(self)
def BeginContact(self, contact):
fixture_a = contact.fixtureA
fixture_b = contact.fixtureB
body_a, body_b = fixture_a.body, fixture_b.body
ud_a, ud_b = body_a.userData, body_b.userData
pedestrian = None
car = None
for ud in (body_a, body_b):
if isinstance(ud, Pedestrian):
pedestrian = ud
elif isinstance(ud, Car):
car = ud
if car is not None and pedestrian is not None:
if began:
print("It does stuff")
else:
print("It does something")
def EndContact(self, contact):
pass
def PreSolve(self, contact, oldManifold):
pass
def PostSolve(self, contact, impulse):
pass
box2world = world(contactListener=myContactListener(),gravity =(0.0, 0.0), doSleep =True)
and I apply this in given classes (only class Pedestrian shown as example for simplicity):
class Pedestrian():
def __init__(self,box2world, ped_velocity =25, position =None,):
if position == None:
position = [5,5]
self.ped_velocity = ped_velocity
self.position = position
self.box2world = box2world
self.nearest_building = 0
self.body = self.box2world.CreateDynamicBody(position = position,
angle = 0.0,
fixtures = b2FixtureDef(
shape = b2CircleShape(radius = 1),
density = 2,
friction = 0.3,
filter = b2Filter(
categoryBits=PEDESTRIAN_CATEGORY,
maskBits=BUILDING_CATEGORY + CAR_CATEGORY,
groupIndex=0,
)))
self.current_position = [self.body.position]
self.body.userData = {'obj': self}
And then I draw the bodies and run the world using pygame
But I am confused about how to continue, how could I use the information from the collisionfilter to be able to for example print the sentence about the accident from above?
Thank you very much
EDIT: I have found a link which solves exactly what I want to do, but it is written in C++ and I do not understand it
http://www.iforce2d.net/b2dtut/collision-callbacks

hey I just answered your question on stackexchange :-)
For collisions it's easy:
local filterData = {
categoryBits = player,
maskBits = wall + nme + platform,
groupIndex = 0
}
fixture:setFilterData(filterData)
player, wall, nme, ... are integers variables (must be power of 2 numbers):
player = 1
wall = 2
nme = 4
... = 16, 32, 64, 128, 256, ...
categoryBits = main object you want to test collisions on
maskBits = you add (with +) all the numbers the main object can collide with.
It's better to store the numbers as variables otherwise it would look like:
local filterData = {
categoryBits = 1,
maskBits = 2 + 4 + 8 + 16 ...,
groupIndex = 0
}
fixture:setFilterData(filterData)
:-)

to answer your second comment that is more complex so I add it as another answer.
You don't handle collisions yourself, Box2D does it for you but you need to set things up.
- create your world
world = b2.World.new(0, 24, true)
- attach listeners to your world (for collisions handling)
world:addEventListener(Event.BEGIN_CONTACT, self.onBeginContact, self)
world:addEventListener(Event.END_CONTACT, self.onEndContact, self)
world:addEventListener(Event.PRE_SOLVE, self.onPreSolveContact, self)
world:addEventListener(Event.POST_SOLVE, self.onPostSolveContact, self)
- in your game loop you need to call Box2D update
self.world:step(1/60, 1, 1)
- then ANSWER IS HERE you check collisions for each objects in those box2d functions listeners
-- collisions handler
function LF_Bullet:onBeginContact(e)
local bodyA = e.fixtureA:getBody()
local bodyB = e.fixtureB:getBody()
if bodyA.type == 100 or bodyB.type == 100 then
self.removebullet = true
end
if bodyA.type == 200 and bodyB.type == 100 then
bodyA.isdead = true
end
if bodyA.type == 100 and bodyB.type == 200 then
bodyB.isdead = true
end
if bodyA.type == 201 and bodyB.type == 100 then
bodyA.isdead = true
end
if bodyA.type == 100 and bodyB.type == 201 then
bodyB.isdead = true
end
end
function LF_Bullet:onPreSolveContact(e)
end
function LF_Bullet:onPostSolveContact(e)
end
function LF_Bullet:onEndContact(e)
end
This is a quick example written in LUA using gideros mobile http://giderosmobile.com/.
A must website regarding box2d is of course:
https://www.iforce2d.net/b2dtut/
It is a vast subject and you may want to follow some youtube tuts. Even if it is not written in py, box2d works the same so you just have to adapt to py.
Some links that may help:
https://www.youtube.com/playlist?list=PLZm85UZQLd2SXQzsF-a0-pPF6IWDDdrXt
That's how I learned using box2d. Hope that helps?

Related

Global variables not carrying across FUNCTIONS

(For those who saw this question the last time I asked it, I sincerely apologize, I used the term "module" when I meant "function", but thank you for your very helpful advice nontheless! I'll make sure to keep it in mind when I begin to add other files into the equation.)
I'm trying to make a text based adventure game using python, and as a result it requires a lot of variables, and as backtracking is a must, I need to use global variables for the essential ones. I have run into speed bumps when trying to get these to be read by other functions. This is the line of code used to define the universal variables, and their starting value
def reset():
global gold, exp, etnl, maxHP, curHP, maxmana, curmana, attack, defence, helm, armtop, armbot, boots, gloves, weapons
gold = 0
exp = 0
etnl = 100 #exp to next level
maxHP = 50
curHP = 50
maxmana = 10
curmana = 10
attack = 5
defence = 5
helm = "none"
armtop = "none"
armbot = "none"
boots = "none"
gloves = "none"
weapon = "fists"
And for example, when I try to display one of the global variables, it shows up as the variable being undefined, as shown here:
def gamestart():
clear() #this command is fine, simply to make it look neater when it is run again
print("you wake up in a clearing in the forest, you can't remember what happened.")
print("you feel numb, you realize you're lying flat on your back.")
print
print("HP " + str(curHP) + "/" + str(maxHP))
Can someone help me out with this?
Is there an easier way to do this?
All help is appreciated!
(yes, I make sure to run the reset function before the newgame function)
A much simpler version if this, at least according to me is:
def variable():
global foo
foo = 7
def trigger():
variable():
output():
def output():
print(foo)
You could store those things into a class used as storage-container. If you declare them classvariables and any accessors as #classmethods you do not need an instance.
class GameState:
gold = 0
exp = 0
etnl = 100 #exp to next level
maxHP = 50
curHP = 50
maxmana = 10
curmana = 10
helm = "none"
armtop = "none"
armbot = "none"
boots = "none"
gloves = "none"
weapon = "fists"
weapons = {"fists":(5,5),"sword":(15,12),"mace":(30,3),"cushion":(2,20)}
#classmethod
def reset(cls):
cls.gold = 0
cls.exp = 0
cls.etnl = 100 #exp to next level
cls.maxHP = 50
cls.curHP = 50
cls.maxmana = 10
cls.curmana = 10
cls.helm = "none"
cls.armtop = "none"
cls.armbot = "none"
cls.boots = "none"
cls.gloves = "none"
cls.weapon = "fists"
#classmethod
def attack(cls):
return cls.weapons.get(cls.weapon,(0,0))[0]
#classmethod
def defense(cls):
return cls.weapons.get(cls.weapon,(0,0))[1]
for w in State.weapons:
State.weapon = w
print("{} has attack {} and defense {}.".format(w, State.attack(),State.defense()))
Output:
fists has attack 5 and defense 5.
sword has attack 15 and defense 12.
mace has attack 30 and defense 3.
cushion has attack 2 and defense 20.
You might want to seperate some things out - f.e. an extra class for the weapon/damage/defense related stuff ...
More reading:
What is the difference between #staticmethod and #classmethod?
https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables
Instead of global variables have you considered storing all the stats in a class/struct? Create an instance of the class at the start of the game, with its default values being specified in the constructor.
G = StartClass()
def gamestart():
print("you wake up in a clearing in the forest, you can't remember what happened.")
print("you feel numb, you realize you're lying flat on your back.")
print("HP " + str(G.curHP) + "/" + str(G.maxHP))
Alternatively, declaring G globally and passing it into gamestart(G) and/or re-instantiating in the reset() function might be options.
Here is a simple example of what I think you are trying to accomplish. If you are using global variables, then you need to be sure you are not inadvertently creating local variables with the same names in your functions (when you mean to be modifying the global variable).
You should look at using classes which I think would help you with some of the semantic confusion here.
value = 10
def reset():
global value
value = 10
def start():
print(f'initial value: {value}')
global value
value += 1
print(f'updated value: {value}')
reset()
print(f'reset value: {value}')
start()
# OUTPUT
# initial value: 10
# updated value: 11
# reset value: 10

Pygame Beat-Em-Up Game // Enemy Attack Timer Never Reaches Zero

Pastebin Link
I am working with Pygame on a beat-em-up game and I'm running into an issue where the enemy will grab the player, forever and not let him go. A timer is supposed to run and when it reaches zero the enemy is supposed to push the player away, but it never reaches zero.
The variable contact is group of enemies who are actually touching the player. It's using the pygame.sprite.Group() function. If the sprites overlap (of the player and any enemy in the list of all enemies), then they are added to the group. If the sprites stop overlapping (enemy walks away, or the player walks away), then the enemies are removed from that group.
contact = pygame.sprite.spritecollide(heroBoonrit, enemy_list, False)
For every tick of the clock, I have it set up to check if there are any enemies touching the player, and if there are, then go through every one of them and see if any of the enemies are in a grabbing state (villan.grabbing_cooldown). It's just an on/off switch that means that the enemy is currently attacking with a grab move. I could probably come up with a more logical variable name.
If those conditions are met, then a few things happen, such as snapping the player's position up (or down) in order to be on the same y coordinate as the enemy. The variable heroBoonrit.held_cooldown is another on/off switch that means that he is currently being held. Both the player and the enemies have their own variable called heroBoonrit.held_countdown and villan.grabbing_countdown respectively. The problem I'm seeing (by running print() for diagnostics is that the enemy countdown decrements by 1 and then it stop decreasing, whereas the countdown timer of my hero decrements until 0. So therefore the enemy never lets go.
I have a feeling that there's probably a much more elegant and clean way to go about handling player and enemy behavior rather than putting on/off switches for the player that relate to whether or not he's being stunned, grabbed, etc (and additionally, corresponding countdown timers for those), but I've only been doing programming for a month and would appreciate any feedback. Maybe after every tick of the clock, the enemy holding timer is reset to 60 again...I have read in other posts that when you are using the Pygame Group() function, you can't easily iterate over the group. Still learning many of the ins and outs...
I ran this command in my main game loop to find out that enemy's grabbing countdown timer only goes from 60 to 59 and then stops counting down:
print("||Shit Clown|| Grabbing = ", enemyShit_Clown.grabbing_cooldown, " Countdown = ", enemyShit_Clown.grabbing_countdown, "||Boonrit|| Grabbed = ", heroBoonrit.held_cooldown, " Countdown = ", heroBoonrit.held_countdown)
Here is the block of code where I'm running into the problem.
for villan in contact:
for villan in enemy_list:
if villan.grabbing_cooldown:
heroBoonrit.held_cooldown = True
heroBoonrit.rect.y = villan.rect.y
heroBoonrit.rect.x = (villan.rect.x + 30)
heroBoonrit.held_countdown = villan.grabbing_countdown
villan.grabbing_countdown -= 1
heroBoonrit.held_countdown -= 1
if villan.grabbing_countdown <= 0:
villan.grabbing_countdown = 0
heroBoonrit.held_countdown = 0
villan.grabbing_cooldown = False
heroBoonrit.held_cooldown = False
heroBoonrit.rect.x += 30
elif villan.attacking_cooldown:
if heroBoonrit.blocking != True:
heroBoonrit.hp -= 100
else:
heroBoonrit.hp -= 10
Here is the Enemy class:
class Enemy(pygame.sprite.Sprite):
def __init__(self, name, level, speed, hp, stamina, fear, blocking_cooldown, jumping_cooldown, attacking_cooldown, held_cooldown, knockdown_cooldown, stun_cooldown, jumping_countdown, attacking_countdown, held_countdown, knockdown_countdown, stun_countdown, grabbing_cooldown, grabbing_countdown, blocking_countdown):
super().__init__()
self.image = pygame.image.load(os.path.join('res','img','chars','shit_clown-3.png'))
#self.image = pygame.Surface([30,20])
#self.image.fill(color)
self.rect = self.image.get_rect()
self.surf = pygame.Surface((30,20))
self.name = name
self.speed = speed
self.level = level
self.hp = hp
self.stamina = stamina
self.fear = fear
self.blocking_cooldown = blocking_cooldown
self.jumping_cooldown = jumping_cooldown
self.jumping_countdown = jumping_countdown
self.attacking_cooldown = attacking_cooldown
self.attacking_countdown = attacking_countdown
self.held_cooldown = held_cooldown
self.held_countdown = held_countdown
self.knockdown_cooldown = knockdown_cooldown
self.knockdown_countdown = knockdown_countdown
self.stun_cooldown = stun_cooldown
self.stun_countdown = stun_countdown
self.grabbing_cooldown = grabbing_cooldown
self.grabbing_countdown = grabbing_countdown
self.blocking_countdown = blocking_countdown
blocking_cooldown = False
jumping_cooldown = False
jumping_coutndown = 0
attacking_cooldown = False
attacking_countdown = 0
held_cooldown = False
held_countdown = 0
knockdown_cooldown = False
knockdown_countdown = 0
stun_cooldown = False
stun_countdown = 0
grabbing_cooldown = False
grabbing_countdown = 0
blocking_countdown = 0
And to spawn the enemy:
enemyShit_Clown = Enemy("Shit Clown", 1, 4, 1000, 10, 90, False, False, False, False, False, False, 0, 0, 0, 0, 0, True, 60, 0)
enemyShit_Clown.image = pygame.image.load(os.path.join('res','img','chars','shit_clown-3.png')).convert()
enemyShit_Clown.rect.x = 300 #random.randrange(300, 400)
enemyShit_Clown.rect.y = 300 #random.randrange(200, 400)
enemy_list.add(enemyShit_Clown)
all_sprites_list.add(enemyShit_Clown)
Thanks very much for your help
I don't see any obvious reason for the villain to stop counting down. That said, I do see that you are decrementing the countdown both in the hero.update code and in the main loop. I'd expect your hero to count down twice as fast as the villain, but not 60 times as fast.
I'd like to suggest some code changes. First, get rid of most of the parameters in your __init__ code. Just set the default values to 0 or False or whatever.
Next, I see you creating a character, and then assigning an image to it. That should be in the constructor, with maybe a defaulted parameter to select a different starting image.
Next, you have countdown code in the hero's update method, but not in the villains. I think this is a mistake. Move the countdowns into the update routine, and don't worry about searching for it in the main loop.
Finally, there is a rule of thumb for OO programming you are missing: "Tell, don't ask."
Basically, this says, among other things, that you shouldn't be accessing the attributes of another object, and you definitely shouldn't be modifying the attributes of another object.
I suggest you write methods for this stuff, like so:
class Villain:
def grab(self, hero):
"""Start a grabbing attack on hero, if not blocked/busy"""
if self.grabbing:
# Already grabbing someone. Fail.
return False
ticks = 60
if hero.grabbed(self, ticks):
self.grabbing = hero
self.grabbing_countdown = ticks
hero.moveto(self.rect.y, self.rect.x + 30)
return True
else:
return False
def update(self, *args):
:
blah blah blah
:
if self.grabbing:
self.grabbing_countdown -= 1
if self.grabbing_countdown == 0:
self.grabbing.release(self)
self.grabbing.push_away(self.rect.x, self.rect.y, 30)
self.grabbing = None

Trying to get text for a "game" where it should refresh/randomize HP and DMG values, but is not

Really new to Python and I'm stuck. I can't figure out how to get HP and DMG to randomize when it's called when the button I've created is clicked.
Here's currently what I have:
# find your fav character images and pass it here
Char1 = Character('Snart.png','CAPTAIN COLD',DISPLAYSURF,(100,300),200)
Char2 = Character('Flash.png','FLASH',DISPLAYSURF,(700,300),200)
def displayButtons(bList):
for x in bList:
x.display()
def main():
B1.active = True
clickCount = 1
B2.active = False
while True:
DISPLAYSURF.fill(BGCOLOR)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
## MOUSE EVENTS
elif event.type == MOUSEBUTTONDOWN:
mouse = pygame.mouse.get_pos()
if B1.clicked(mouse):
B1.highlight = True
print("Hello") ## Just to see if it actually get's pressed
clickCount = 2
elif B2.clicked(mouse):
B2.highlight = True
print("Bye") ## Just to see if it actually get's pressed
clickCount = 1
elif event.type == MOUSEBUTTONUP:
if B1.clicked(mouse):
Char1.Randomize() ## Randomize the HP DMG
B1.highlight = False
B1.active = False
B2.active = True
elif B2.clicked(mouse):
Char2.Randomize() ## Randomize the HP DMG
B2.highlight = False
B2.active = False
B1.active = True
Char1.display()
Char2.display()
displayButtons(BUTTONLIST)
pygame.display.update()
main()
And for the class that it's creating:
class Character(object):
def __init__(self, imagefile,charname,surf,pos,scalesize):
self.SURF = surf
self.POS = pos
self.IMAGESURF = pygame.image.load(imagefile)
self.IMAGESURF = pygame.transform.scale(self.IMAGESURF, (scalesize,scalesize))
self.HP = (0, 300) # should range from (0 - 300) ## randint: return a random integer(start,stop)
self.DMG = (0, 100) # should range from (0 - 100)
self.GameFont = pygame.font.SysFont("Sylfaen", 50)
# this text has a black background. Can you make it transparent ?. DONE
self.NAME = self.GameFont.render(charname, True,(255,255,255),None)
self.Randomize()
self.__drawText()
self.__displayText()
# complete this function
# this function should randomize HP, DMG and should display on the screen
# this function should be called on a button press
def Randomize(self):
#pass
self.HP = randint(0, 300)
self.DMG = randint(0, 300)
## DON'T UNCOMMENT UNLESS YOU WANT IT TO RANDOMLY GENERATE NON-STOP
## self.HPText = self.GameFont.render('HP : ' +str(self.HPrand), True,(255,255,255),None)
## self.DMGText = self.GameFont.render('DMG: ' +str(self.DMGrand), True,(255,255,255),None)
def __displayText(self):
self.SURF.blit(self.HPText,(self.POS[0]+200,self.POS[1]+50))
self.SURF.blit(self.DMGText,(self.POS[0]+200,self.POS[1]+150))
self.SURF.blit(self.NAME,(self.POS[0]+20,self.POS[1]-100))
# fix the error in this function, DONE
def __drawText(self):
# this text has a black background. Can you make it transparent ?.
self.HPText = self.GameFont.render('HP : ' +str(self.HP), True,(255,255,255),None)
self.DMGText = self.GameFont.render('DMG: ' +str(self.DMG), True,(255,255,255),None)
# fix the errors in this function, DONE
def display(self):
self.Randomize()
self.__displayText()
self.SURF.blit(self.IMAGESURF,self.POS)
After you randomize the HP and DMG values, you need to re-render the text values for each of them. You have a function to do that, named __drawText, but you're not calling it when the button is pressed. This is why you keep drawing the old values even after Randomize has been called.
I'm not sure exactly how you want your class to work, but perhaps __drawText should be called from Randomize? You can't rely upon the external code that that runs Randomize to also call __drawText since you've given it a name starting with two underscores (which invokes Python's name mangling system). If it's supposed to be part of the class API, you certainly don't want to be doing that. External code can still call __drawText, but only by manually doing the mangling (and calling e.g. Char1._Character__drawText).
One final thing, which is unrelated to your current issues. Your variables are being named in a way that is a bit unusual for Python code. The more usual Python style is to use lowercase_with_underscores names for most variables and functions (including methods), and reserve TitleCase names for classes. ALL_CAPS is used occasionally for variables that are notionally constant, but the regular variable style is also pretty common even for constants (e.g. math.pi).
Using a different naming convention doesn't make your code wrong, but it may be harder for other programmers to follow than if you followed the standard conventions. See PEP 8 for the style used for the official Python interpreter. A lot of other Python code follows that guide (with perhaps a little more leeway given on line lengths). Google also has a Python style guide, which is (as far as I can tell) pretty much compatible with PEP 8.

Change object's variable from different file

I want to access object (specifically its variables) from functions defined in different file. Let's see an example:
File 1 - grail.py
import enemies
class Encounter:
def __init__(self):
self.counter = 1
self.number = 0
self.who = "We've encountered no one."
def forward(self):
if self.counter == 1:
enemies.knightofni()
elif self.counter == 2:
enemies.frenchman()
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
knight = Encounter()
for i in range(4):
print(str(knight.number) + " " + knight.who)
knight.forward()
File 2 - enemies.py (I probably need something in this file)
def knightofni():
Object.number = 1
Object.who = "We've encountered Knight of Ni."
def frenchman():
Object.number = 4
Object.who = "We've encountered French."
Output should show:
0 We've encountered no one.
1 We've encountered Knight of Ni.
4 We've encountered French.
42 We've found the Grail!
I know you can achieve the output by returning something from functions in file enemies.py, for example function frenchman() could look like:
def frenchman():
return [4, "We've encountered French."]
and in grail.py I should change code to collect what the frenchman() returns:
...
elif self.counter == 2:
spam = enemies.frenchman()
self.number = spam[0]
self.who = spam[1]
...
but it uses additional resources, makes the code longer, and more cumbersome in more complicated situations.
Is there a way to do the job directly on the object's variables but keeping functions in separate file?
EDIT
There are already answers to this question but maybe I will add clarification seeing doubt in one of the answers (citing comment to this answer):
I want it to be possible to add other "enemies" without making lengthy code in this place (so forward() is kind of a wrapper, place where it is decided what to do in different situations). It is also more readable if this functions are in different file.
Think of situation where there would be 100 "enemies" and each would need to change 100 variables which are lists with 1M entries each. Is there a better way than putting "enemies" into other file and changing variables directly in the file?
Problem
You need to hand over the object as argument.
In the function:
def knightofni(obj):
obj.number = 1
obj.who = "We've encountered Knight of Ni."
and when using it in the class:
enemies.knightofni(self)
Do the same for frenchman().
Full code
grail.py
import enemies
class Encounter:
def __init__(self):
self.counter = 1
self.number = 0
self.who = "We've encountered no one."
def forward(self):
if self.counter == 1:
enemies.knightofni(self)
elif self.counter == 2:
enemies.frenchman(self)
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
knight = Encounter()
for i in range(4):
print(str(knight.number) + " " + knight.who)
knight.forward()
and enemies.py:
def knightofni(obj):
obj.number = 1
obj.who = "We've encountered Knight of Ni."
def frenchman(obj):
obj.number = 4
obj.who = "We've encountered French."
Output:
0 We've encountered no one.
1 We've encountered Knight of Ni.
4 We've encountered French.
42 We've found the Grail!
It is possible to do this, though I don't know why you would really want to do it this way.
In your forward and __init__ methods you'll notice that you are passing in self, which is the instance of Encounter you are operating on. That is why you can do self.number = 42 and get the correct number when you call knight.number.
Since self is just an object you can pass it into the functions in 'enemies.py'.
Try:
# grail.py
def forward(self):
if self.counter == 1:
enemies.knightofni(self)
elif self.counter == 2:
enemies.frenchman(self)
else:
self.number = 42
self.who = "We've found the Grail!"
self.counter += 1
#enemies.py
def knightofni(that):
that.number = 1
that.who = "We've encountered Knight of Ni."
def frenchman(that):
that.number = 4
that.who = "We've encountered French."

How to save buildings at specific coordinates in python?

So I do know how to save the class's x,y coordinates but I don't know how to save buildings at the coordinates my player has been at. I'll attempt to make this more clear.
I'm making a text-based. To move your player you either type left,right,up,or down. It will therefore change your x and y accordingly.
Ex: To move up it adds 1 to the y value of the player class. player.yPos += 1 . However if the player goes to the point 0,1 and then 0,2 but moves back down to 0,1 and there was a building at the point 0,1 how do I make sure it's still there when the player goes back? I've been thinking I'll have to store all of the player's x,y movements in to a list. I don't know how to make the positions of that list equal the object that will be there though. If this doesn't make sense I can attempt rewording.
P.S. Please use the most simple logical explanation possible. Generally when I read something on stackoverflow I want to jump off of a cliff with how involved it is. (Basically, dumb it down for me please!)
class player:
Cnuts = 0
statPoints = 0
pStrength = 0
pConstitution = 0
pQuickness = 0
pIntelligence = 0
pStamina = 0
playerName = ''
Weapon = 0
Armour = 0
Shield = 0
inventory = []
xPos = 0
yPos = 0
#Building Code
class Entity:
id = 0
xPos = 0
yPos = 0
name = ''
description = ''
def __init__(self, id, xLocation, yLocation, name, description):
self.xLocation = xLocation
self.yLocation = yLocation
self.id = id
self.name = name
self.description = description
class Structure(Entity):
pass
I haven't decided what goes in to the Structure/Building class because I don't know what else it needs other than what Entity already has. I have another class for monsters that also inherits from Entity which is why I have it.
#Map code
isExploring = True
def Map():
while isExploring == True:
pInput = input('What direction would you like to go?')
if pInput == 'Up':
player.yPos += 1
elif pInput == 'Down':
player.yPos -= 1
elif pInput == 'Left':
player.xPos -= 1
elif pInput == 'Right':
player.xPos += 1
elif pInput == 'Diagonal Left':
player.xPos
player.yPos
elif pInput == 'Diagonal Right':
pass
elif pInput == 'Down Diag Left':
pass
elif pInput == 'Down Diag Right':
pass
Thanks for the help in advance!
I don't see code for a building, but I'm guessing the building will eventually inherit from Entity (Player should also inherit this class). An entity object has self.xLocation and self.yLocation, so this makes it a bit easier to implement a location aware player. So what you do is that the class you make for building has to implement the __hash__ method, so something like this.
class Building(Entity):
def __hash__(self):
return hash(tuple(self.xLocatioin, self.yLocation))
def __eq__(self, other):
return isinstance(other, Building) and hash(self) == hash(other)
The function is called __hash__ because python recognizes this special keyword as meaning that the building object can be placed in a dictionary. So whenever you try to place a Building object in a set or use it as a key for a dictionary, python will automatically call it's __hash__ method, and use that value to determine the position in which to place the object in the set/dictionary. Implementing hash usually means implementing __eq__ which is another magic function that python automatically calls when you compare 2 objects using the == operator.
The player class will then store each building it has visited in a set, which can then be queried to determine if a building has been visited before
class Player(Entity):
def __init__(self, *args):
super.__init__(self, args)
self.visited = set()
self.currLocation = tuple(self.xLocatioin, self.yLocation)
def visit(self, entity):
if not beenHere(entity):
self.visited.add(entity)
self.currLocation = tuple(entity.xLocatioin, entity.yLocation)
def beenHere(self, entity):
return entity in self.visited
And there you have it, now the player object can determine which building it has visited before or not

Categories