Hi all, I am putting together a collection of sample files for the 2.55 game engine that intend to serve as learning material for people who is new to the Game Engine (and for all the 2.49 people who are afraid of touching the new 2.5 interface). One of the main problem with the BGE seems to be the lack of good quality ‘components’ for people to reuse, and beginner’s tutorials are rarely advanced enough to get people through a real big production. This is my way of trying to change that…
My aim is to prepare at least 12 files before Christmas. If this is not enough, you can always, oh I don’t know, get a book on the topic or something. :)
You are looking at a flat plane consists of 2 triangles running in the Blender 2.5 Game Engine. There is no run-time subdivision, no geometry shader, using only a minimum pass-through vertex shader, all the magic is happening in the pixel shader. The terrain is completely 3D looking, you can fly around it, view it from different angles, there is lights, shadows, bumps, shiny lakes, and even a height-based fog. Below is a video of the technique in motion:
Isn’t Relief Mapping great?
Apart from all games that will inevitably take advantage of this technique, another application I can think of that will reap huge benefit from this is the loading and displaying of large-scale terrain visualization at virtually no CPU overhead. A 4096×4096 DEM texture can be easily displayed at 60fps. Whereas if the same data were to be converted to real geometry, most computers will die trying to render those 33million triangles.
The effect is achieved by a rather complex fragment shader (pixel shader) that effectively does ray-casting on each pixel to determine the location and visibility of each pixel. The shader requires at least 2 textures. A diffuse color texture; and a normal-height texture that contains the surface normal stored in the RGB channel and the height value stored in the Alpha channel. Supplied with a miminum of these two textures, the shader is able to reconstruct the 3D surface like you saw. (I also used a specular texture to control the specularity of the lakes, but this is optional)
A long time ago I toyed with the idea of making an entire game by myself. Being a driving game, I spent a long time with the basics stuff like vehicle physics to make sure the driving part is stable, bug-free, and most importantly, fun.
I never finished it. But ever since I posted the above video, I have been getting a lot of inquiries as to how I did the vehicle physics system in Blender. So here is a quick guide followed by the sample file.
The key to stable driving dynamics is to use the built-in PhysicsConstraints module within Blender’s game engine. PhysicsConstraints is a python wrapper for the Bullet physics engine. No any homemade setup I’ve seen is as good as what’s already provided by Bullet Physics. So we’ll use that. Because there isn’t a graphical interface for setting up the vehicle physics yet in Blender, we need to write a few lines of Python script. The key component is the car body object, which is linked to the 4 wheels through a logic brick connection so that the script would recognize the linked objects as wheels. (As you can see, all the actual driving logic is all happening on the car body, the wheels have no real logic brick attached).
The entire setup is a bit complex, since there is a mix of logic brick and python. Take a look at the sample file if you are lost. When one runs the game, the following happens:
0. World is initialized, but we don’t really care about that here.
1. The car body is initalized, scripts.carInit() is ran. In this script, it initializes the car as a “vehicle constraint”, aka constraint type11, and stores it as a Python object called vehicle. The same script then looks for the 4 wheel by access a gamelogic actuator with specific names (in this case, wheel1, wheel3, etc), then the script attaches the wheels to the car body using the settings specified in the script. Variables such as RollInfluence, SuspensionStiffness, and TyreFriction can all be set on a per-tire basis once the vehicle object is created. The job of carInit() is now done. Our car body is now considered to be a vehicle by the Blender game engine, and it will behave like one.
2. Every frame, scripts.carHandler() is ran. This script does the actual moving of the car, it applies engine force and steering to the vehicle object. But this script gets the user input (keyboard sensor inputs) from another source. (See #3 below) Vehicle objects have methods such as applyBreaking(), applyEngineForce(), getWheelRotation(), getNumWheels, which you can all call.
3. Every time a key is pressed, script.keyHandler() is ran. It figures out which key is pressed and set the intermediate variable so that #2 (i.e. scripts.carHandler) would know how much throttle to apply, where to steer, etc. This script is separated from scripts.carHandler() not because of technical limitations, but by design so that it’s easier to manage the code.
That’s all there is to it. If the script layout is a bit confusing, it’s because it is. I originally intended it to be a bigger project, thus everything is separated into nice neat functions. Again, you can DOWNLOAD the whole setup for Blender 2.5 from here.
Controls:
Arrow Keys to move the car
Space bar for handbrake
R to reset car if it flips over
number row 1,2,3,4,5 to change camera
I had been lucky enough to be a part of a very cool project taking place at the Fisheries Centre at the University of British Columbia. In a nut shell, we are trying to create a 3D underwater visualization using the Blender Game Engine to display scientific data in a more pleasing way. Basically translating a stream of mind-numbing data:
into something prettier:
The visualization data is coming from EwE6, a well-respected ecosystem modeling software. Blender uses inputs such as biomass, water turbidity and light level to create a realtime visualization that allows the user to ‘swim’ around in the ocean, watch the schools of fish interact, and see their population change over time.
To accomplish all this, the project consists of 3 layers:
The server core does all the heavy computation while the visualization layer does all the graphics, AI, as well as user interaction. To facilitate the communication across these two separate processes, a third layer (called the GameClient) is created, providing the bridge.
Being a Blender artist, my main focus is on the visualization layer: the Blender Game Engine. In the following few posts, I will outline the process in creating this application, and describe some of the issues we faced. Please leave a comment if you are interested, it will motivate me to write more :)
A while ago, I posted a simple demo I made in the Blender game engine that let’s 2 player interact with each other over the internet, all it is required is a really simple networking script and the IP address of the two players. Here is as youtube video of it in action, mouse-over the video to see some caption text.
The laptop on the left is physically separated from the desktop to the right, linked only by a wireless internet connection. And as you can see, input made to the desktop computer is sent across the network to the laptop, in realtime.
And here is the script, I’ll try to explain everything as best I can:
# Simple Python UDP networking demo Created by Mike Pan
# Todo: auto-self IP detection would be nice
# the following line loads the game module used by Python
# to access the Blender Game Engine API, and assigns it the alias G
import GameLogic as G
# load the network socket python module, we'll need it later
import socket
# define own IP address, and stores it as a property under GameLogic
# essentially making it accessible as a global variable
G.ownIP = "xxx.xxx.xxx.xxx"
# define peer IP address, stores it as a property under GameLogic
# again, essentially making it accessible as a global variable
G.peerIP = "xxx.xxx.xxx.xxx"
# define the port number (arbitrary), and the socket buffer size
# (4096 is a good starting point)
G.port = 4000
G.buffer = 4096
# create an IPV4 address with the pre-defined IP and the port number
addr = (G.peerIP, G.port)
# We'll add this line to all our communication, so when debugging,
# we won't get confused as to who send the packets.
signature = "Sent from " + G.ownIP
def init():
# create an UDP socket for **sending** datagram
G.sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# time out is 10ms, pretty short for realworld application,
#but enough for debugging over local network
G.sender.settimeout(0.01)
# this is important! by making this socket object a
# non-blocking operation, all network stuff is run on
# a separate thread, thus any network delay will not
# cause the game to slow down
G.sender.setblocking(0)
# creates UDP socket for **receiving** datagram
G.listener = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# same as above, time out is 10ms
G.listener.settimeout(0.01)
# same as above, we also need to make sure network operations
# will not slow down the main application
G.listener.setblocking(0)
# Win32 needs the socket binded:
try:
G.listener.bind((G.ownIP, G.port))
except:
print "Binding Listener failed"
def comm():
# get the object that is attached to this script
own = G.getCurrentController().owner
# receive from peer
try:
data, port = G.listener.recvfrom(G.buffer)
# data contains all the data that came through,
# it's a string, and we can do whatever we want
# like print it out
print data.split(',')
# if the receiving fails, print something to notify the user
except:
print "Receive failed"
# send to peer
# generate data to be sent here,
# send the position data across the network:
string = str(own.worldPosition)+ ',' + signature
# going to try to send the data
try:
G.sender.sendto(string, addr)
# if sending fails, notify with error message
except:
print "Send failed"
That’s it! To use this script, we simply need to call init() once at the start of the game, and then comm() everytime you wish to send/receive some data (perhaps every 1/30th of a second). As mentioned, here is no ‘server’ involved, the two peers connect direction to each other via a UDP socket. All the user has to do is specify the two IP addresses.