StarBlender

|
StarBlender
tut4
GAME BLENDER & PYTHON
1/2
This tutorial describes the steps to make a script which
allows you to move an object to a precise location defined by an
Empty which, in turn, is controlled by the mouse (a technique
commonly used in strategy games). However, it is also an
especially good way to familiarize yourself with the use of
Python in Game Blender. Excerpts from the script in this tutorial
will be printed in blue.
Useful commands (Text/Script Editor Window):
Copy: alt-C
Paste: alt-V
(Note: the following commands are only required if you are
using an European-style AZERTY keyboard. For Western-style QWERTY
keyboards, simply type in the characters as you normally would).
Character " [ ": press " ^ " on a QWERTY
keyboard (configures an option to switch from an AZERTY to a
QWERTY keyboard)
Character " ] ": press " $ " in QWERTY
Character " # ": press " * " in QWERTY (When
this character is inserted at the beginning of a line, it means
that the subsequent text on that line is treated as a comment and
therefore is not acted upon in the script).
1°) Let's get started immediately by tackling the hardest
part of this tutorial: the script. Create a new text file in a
suitable window (shift-F11 then click on and select ADD
NEW from the menu). In the Text/Script Editor Window header,
click just after the text on the button which currently reads 'TX:
Text', then [Backspace] over the existing text and type in 'globLoc'
(without the quotes) to give the script a more suitable name. At
the beginning of each script, you must import the modules which
are utilized in the script. Here we will need those which are
included with Game Blender (normal) and Rasterizer (which
includes/understands all matters that concern displaying within
the 3D window). For that, we need to type:
import Rasterizer
import GameLogic (or
from GameLogic
import * , your choice)
Since Python is case-sensitive, be carefull when typing in
CAPITAL and lower-case letters, to avoid this type of error in
your script. Syntax is very important in scripts.
For script functions involving the mouse, it is necessary to
take into account the size of the 3D window to calculate the
position of the cursor. To accomplish this, we can utilize the
Rasterizer module which contains functions to determine
the dimensions of the window. Therefore, we will require the
following functions:
l = Rasterizer.getWindowWidth()
L = Rasterizer.getWindowHeight()
The purpose of the functions are fairly self-evident from their
names, the first line is for the width and the second, the height.
As is shown above, to assign a value to a variable, requires only
a single "=" (Equal sign character) between them. A
variable or a module is always required before a function. Here
it is Rasterizer which was placed before the function get...().
(It is necessary to separate the function from the variable with
a period or dot).
Let's perform a test: add the following line:
print l, L (the
print function should be followed by a variable, several
variables can be called by seperating them with a comma)
Select the camera in the default 3D Window (Shift+[F5]). Snap the
camera to the grid, Shift+[s], Sel > Grid, then in the Side
view, Numpad_3, position the camera above the plane and rotate it
90 degrees so that it is directed towards the plane. Switch to
camera view mode, Numpad_0. Then, in the Realtime Buttons Window
[F8], add an "Always" sensor to it, linked to a
Python controller; "Script: " name of script "
".
Start the "game" by pressing [p], (Quit by pressing [Escape])
and look at the DOS window. If all went according to plans, the
dimensions of the 3D window should be displayed. If instead
"PYTHON SCRIPT ERROR" is displayed, check the script,
normally a description of the error is displayed in the DOS
Window just below the error statement.
Got it working? Good, then erase the last line.
2°) We now come to the logic of the game. It is necessary to
declare the Python controller with a variable (ex: cont)
cont = GameLogic.getCurrentController() (if you've entered from GameLogic import * at
the beginning of the script, do not type Gamelogic. ).
Now, we will open up access to the object's data:
ow = cont.getOwner() (as
shown here, you can give any name to the variable. Also, you must
use the previously declared variable "cont")
Then we must create a list of all the sensors and actuators
linked to the controller, which are used in the script.
senslist = cont.getSensors()
actlist = cont.getActuators() (Here once again, the variable "cont" is
used because the function calls are performed with respect to the
controller)
In the logic, there will be a sensor (the position of the
mouse) and an actuator (the position of the Empty)
linked to the Python controller, which will be added in the next
step. However, first we will add the following 2 lines to the
script to complete the preliminary setup portion of the script.
mousesensor = senslist[0] (the value returned from the location specified by
the digit between the square brackets defines the position of the
sensor, note that since lists are "zero-based", that it
starts with 0 zero, NOT 1 one)
newloc = actlist[0] (in
more general terms, to assign elements from a list to a variable,
you access data from the list by using indices which indicate the
position of the data. If you were to try to run the script now,
you will receive an error message "LIST INDEX OUT OF RANGE"
in the DOS Window. This is to be expected, since currently, there
is no actuator linked to the controller. Therefore, add an Empty
with an actuator (any) linked to the controller. Also, rename the
Empty, ie.: 'OB: Empty' as 'Target').
3°) Now that we've completed setting up the frame work for
the script, it's time to begin working on the heart of the script.
Here's a brief description of the principle behind the script:
when the script is triggered by activating the sensor, it
determines the position of the cursor starting from the center of
the screen (ie. of the camera) and then, in general terms,
calculates its position in the scene by adding the co-ordinates
of the camera. Subsequently, when one clicks the left mouse
button, the Empty (Target) will be positioned at these co-ordinates,
therefore at the cursor. That's the basic idea. Therefore, let's
start by obtaining the co-ordinates of the camera:
campos = ow.getPosition() (As used here, the defined variable "ow"
provides access to information of the higher level object, the
function getPosition creates a list of 3 elements, the x, y, and
z co-ordinates)
The current co-ordinates of the mouse on the screen are:
x = mousesensor.getXPosition()
y = mousesensor.getYPosition()
You can test this function by replacing the "Always"
sensor with a "Mouse" sensor with
the option "Trigger on movement" activated. To
display multiple objects, simply select both the Empty and the
Camera and press the 'Sel' button next to the 'Actuators' button.
Target is of course the name of the Empty and
globLoc, that of the script
Also, be sure to add a temporary test print statement to
check the script:
print x, y
4°) You will notice that the co-ordinate's origin for the
mouse's position is the top left-hand corner, but it would be
preferable if its origin was the center of the screen. Moreover,
to facilitate subsequent calculations, it is necessary to have
the co-ordinates between -1 and 1. Ex:
center: 0; 0
bottom, left-hand corner: -1; -1
bottom, right-hand corner: 1; -1
top, right-hand corner: 1; 1
top, left-hand corner: -1; 1
With a little thought, you can come up with the necessary
formulas:
screenposx = 2.0 * (x - l/2) / l
screenposy = 2.0 * ((L - y) - L/2) / L (Hopefully, you can do the math, since I will not
describe how to derive them - simply try substituting in some
values to see how they work; the only thing which may require
further explanation is the "2.0". Why not simply use
the integer value 2? It is because initially, x and y are
declared as integers (whole numbers) and because the screen
dimensions are also integer values, the integer arithmetic
operation will return an integer result of either 0 or 1, ie. the
decimal value is not retained, therefore you must force the
arithmetic operation to recognize the decimal portion (real or
floating point number) with a ".0" (thanks to Olivier
for this tip))
As usual, to check:
print screenposx, screenposy
5°) Now, it is necessary to convert this position on the
screen into its corresponding position in the scene. The problem
is that when the camera changes height, the mouse no longer
covers the same area: a problem. Therefore, we will need to
introduce a small variable according to the height. Currently,
the postion of the screen corresponds with that of the scene,
only if the camera covers 4 squares (thus the co-ordinates would
vary from -1 to 1, like the mouse). Therefore, we place the
camera at this height and then note its position on the z axis.
Under these conditions, it is enough to divide any height of the
camera by this number, in order to determine the scaling factor (visual)
of the scene and consequently to then multiply the position of
the mouse. Ex:
_ visible scene: from -1 to 1
_ height of the camera: X
_ visible scene: from -2 to 2
_ height camera: 2X
scaling factor: 2
Thus, you would multiply the mouse's co-ordinates by 2 (Hopefully,
you caught all that; I know it's not very clear...;)
I'm sure you could find the "magic number" X, for
yourself, but I'll give it to you (Since I'm such a nice guy:) 1.265
for the width and 1.84 for the height (Of course there are 2
values since the camera is not square; Note: the previous
calculations assume a camera with a lens setting of 35.
Also, if you wish to determine these two numbers for yourself, be
sure that the 3D Window is full screen, ie. only the 3D Window
and the Windows title bar are displayed on the screen, with no
other windows open or header bars). Therefore:
deltax = campos[2] / 1.265
deltay = campos[2] / 1.84 (Note: the 2 corresponds to the third element of the
list "campos" i.e. the height (z axes co-ordinate) of
the camera)
Now, the co-ordinates of the mouse can be converted into global
co-ordinates by adding the position of the camera and then used
to create a list of co-ordinates:
newposx = campos[0] + screenposx * deltax
newposy = campos[1] + screenposy * deltay
newposz = 0
newpos = [newposx, newposy, newposz]
Check:
print newpos (move
the camera, the position of the mouse should produce the global
coordinate values...)
6°) Now the only thing left to do is provide these co-ordinates
to the Empty (Target) so that it is positioned at the
desired place. However, if we were to implement it directly like
that, the Empty would always be positioned under the mouse.
Thus, we will need to add a condition which indicates that the
left mouse button was pressed. Since I did not find this function,
we will use a property "clic" which is not set to
"on", until the the left mouse button is pressed. We
will first finish the script and then implement the corresponding
logic in the 2nd part of the tutorial. Thus, we will begin by
declaring the property "clic":
clic = ow.clic (This
functions in the same way for all of the properties, thus the
variable "clic" has the same value as the property)
Here we will have to open up access to the data of the Empty
in order to give it these co-ordinates, only the Empty is
not the owner of the script. However, if you look towards the
beginning of the script, the definition of the actuator (owned
by the Empty) is assigned to the variable newloc.
Therefore, it is necessary to go through this actuator in
order to manage to obtain access to the object itself:
ow2 = newloc.getOwner()
Now we can write the condition and its effect:
if clic == "on": (Note: To test for an equivalency condition, we need
to use 2 "=" (Equal sign characters); notice that
values which are character strings are inserted between double
quotation marks).
ow2.setPosition(newpos) (Be especially carefull not to forget the tab
indentation, it indicates the hierarchy, in the contrary case, it
would be ignored).
7°) The script is now finished, but as it is, it will not
work, it is still missing a little bit of logic (for the
condition "clic"). However, the hardest part of the
tutorial is done. Here is the complete script:
import Rasterizer
import GameLogic
l = Rasterizer.getWindowWidth()
L = Rasterizer.getWindowHeight()
cont = GameLogic.getCurrentController()
ow = cont.getOwner()
senslist = cont.getSensors()
actlist = cont.getActuators()
mousesensor = senslist[0]
newloc = actlist[0]
campos = ow.getPosition()
x = mousesensor.getXPosition()
y = mousesensor.getYPosition()
screenposx = 2.0 * (x - l/2) / l
screenposy = 2.0 * ((L - y) - L/2) / L
deltax = campos[2] / 1.265
deltay = campos[2] / 1.84
newposx = campos[0] + screenposx *
deltax
newposy = campos[1] + screenposy * deltay
newposz = 0
newpos = [newposx, newposy, newposz]
clic = ow.clic
ow2 = newloc.getOwner()
if clic == "on":
ow2.setPosition(newpos)
(The line spacing is not
important, it simply makes the script clearer).
NEXT
|
StarBlender
Today :
Saturday 12 July 2025 Last update : 2001
Blender version : 2.12

©copyright Abjedi 2001
|