If you recall (or not), the last part of this tutorial series dealt with cleaning up our code - also, there was a supplemental exercise in which you were asked to implement vertical movement by using the up and down arrow keys. That should have been simple enough for you to do yourself, so I will pick up from that point. At this point, we have an initialization of our Player object's code, and we update the Player every frame, moving it about or checking for information.
---------------------------------------------------------------------------------------------------------------------------
from bge import logic
from bge import events
def Player():
cont = logic.getCurrentController()
obj = cont.owner
motion = cont.actuators['Motion']
key = logic.keyboard.events
kbleft = key[events.LEFTARROWKEY]
kbright = key[events.RIGHTARROWKEY]
kbup = key[events.UPARROWKEY]
kbdown = key[events.DOWNARROWKEY]
def Init():
if not 'init' in obj:
obj['init'] = 1
def Update():
movespd = 0.2
mx = 0.0
my = 0.0
if kbleft > 0:
mx = -movespd
elif kbright > 0:
mx = movespd
if kbup > 0:
my = movespd
elif kbdown > 0:
my = -movespd
pos = obj.position # Get the Player's current position
topos = pos.copy() # Make a copy of the Player's current position
topos.x += mx # And offset the copy by the movement value for the X-axis
if obj.rayCast(topos, pos, 0, 'wall', 1, 1)[0] != None: # We just collided with something on the X-axis
mx = 0 # So stop movement on the X-axis
pos = obj.position # Get the Player's current position
topos = pos.copy() # Make a copy of the Player's current position
topos.y += my # And offset the copy by the movement value for the Y-axis
if obj.rayCast(topos, pos, 0, 'wall', 1, 1)[0] != None: # We just collided with something on the Y-axis
my = 0 # So stop movement on the Y-axis
motion.dLoc = [mx, my, 0.0]
cont.activate(motion)
Init()
Update()
---------------------------------------------------------------------------------------------------------------------------
Now, then - as you can see, there are some changes to the code, but nothing especially difficult to comprehend. We added the movement on the Y-axis by using the up and down arrow keys before. We also added in movement on a variable basis, rather than just using numbers. Finally, this time, we'll work on adding in collision checking for the walls. So, the first change is this line:
movespd = 0.2
Here in this line, we create a local movement variable that we will move the Player by - this way, we can have the Player move a uniform amount easily. We could even change how fast the Player moves under certain circumstances by altering this variable later.
if kbleft > 0:
mx = -movespd
elif kbright > 0:
mx = movespd
if kbup > 0:
my = movespd
elif kbdown > 0:
my = -movespd
Now, the movement on the keyboard inputs move by the movespd variable, which is 0.2. The real changes come up next.
pos = obj.position # Get the Player's current position
topos = pos.copy() # Make a copy of the Player's current position
topos.x += mx # And offset the copy by the movement value for the X-axis
if obj.rayCast(topos, pos, 0, 'wall', 1, 1)[0] != None: # We just collided with something on the X-axis
mx = 0 # So stop movement on the X-axis
This time, we see quite a big change. This code (and the next portion) deal with collision. It performs raycasts to determine if there are objects with the property described in the direction described.This code is basically duplicated again for the Y-axis, so I will only explain the first part (the X-Axis collision check).
The first line creates a simple local variable that references the Player object's position. The next line creates a new local variable, topos, that copies the Player's position - this will be the ending position of our raycast function to test for collisions. Next, we add to the X position of the ending position (topos), the movement values we have from the keyboard input. Next, we use the object's rayCast function to test for collisions. The function's arguments are as follows:
1) Ending position of the ray to be tested for collisions.
2) Starting position of the ray to be tested for collisions.
3) Distance of the ray - if the distance is 0, then the ray will end on the ending position of the raycast. Otherwise, the ray will have the specific distance entered.
4) The property to test for collisions - since we are trying to find out if we are moving into a wall, we test for objects that have a wall property. If the property is blank (just quotes), then it will test for any solid object.
5) Whether to return the face normal of the face contacted by the rayCast function.
6) Whether to use X-Ray - with X-Ray mode on, it will look only for objects with the specified property. Otherwise, it will stop on first object.
It's not too complex - if you've noticed, though, we use two separate rayCast functions, one for each axis we move on. Why do that? We test separately because by doing so, we separate stopping for each axis, and so can slide on walls as the movements are separated. If we used one rayCast function, we would have to stop on any collision, regardless of what angle we approach the collision from.
Well, that's all of the changes in the source code! For the next tutorial part, we'll look into fixing the only bug that's arisen - collision only happens from the center of the Player object, and so it intersects the walls partially before collision. There's also a bug of being able to go through corners of walls, though this is simply a by-product of the first bug. Download the source code for part 3 of Using Python with the Blender Game Engine here. So far, so good!
what about assigning a radar or near property to the object and work with the messages they send?
ReplyDeleteYou could work with radar / near sensors. This is a Python tutorial, and so I'm just showing a way to implement this with Python. However, near sensors are a bit heavy on the physics, if I recall, and you'd require at least four sensors to equate to the rayCast functions.
ReplyDeleteThanks for the tutorial - this line:
ReplyDeletemotion = cont.actuators['Motion']
isn't working for me. I tried print(getCurrentController().actuators) and it was empty:
[]
Any thoughts? Thanks again.
Are you sure you connected the actuator to the controller?
ReplyDeleteOh - should have been more careful. Thanks, it's working now.
ReplyDeletethank very much, really nice tutorials for a beginner like me, just one question,i cant find part 4?
ReplyDeleteNo problem. Part 4 is here:
ReplyDeletehttp://solarlune-gameup.blogspot.com/2011/02/using-python-in-blender-game-engine.html
You can find a list of BGE tutorials by clicking that button under the Tutorials section at the right, near the top of the page.
how would you make it so the player can walk on the wall kind of like a wall ride?
ReplyDelete@Le gars qui écrit des choses - Well, it depends on how it would be implemented. You could just suspend the gravity while wall running, in the simplest form.
ReplyDeleteGreat tutorial. I don't understand why the topos variable is chosen for the end of the ray? Why is the offset value significant and why wouldn't you just use a constant. It seems to me you're saying, "shoot out a ray that as long as I have traveled so far on this axis'. Apologies if this sounds abrupt. It's not meant to. - Ian
ReplyDelete