StarBlender tut4



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]
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":

(The line spacing is not important, it simply makes the script clearer).



Today :
Sunday 24 September 2017
Last update : 2001

Blender version : 2.12

Par d�faut

Untitled Untitled Untitled

You think GameBlender is :
without a future.


©copyright Abjedi 2001