Sorry for bringing up an old topic, but I recently came across this script and it’s a great resource from what I can tell so far by using it in my game.
However I have one issue with it, and it is that when the camera knows there’s an object behind it using the Rays, it more or less pulses back and forth instead of getting closer to the character smoothly (kinda hard to explain properly). I’ve tried adjusting the smooth values but it still seems to occur.
Another thing, would there be a way to take the scroll to zoom part of the script? I’m not too great with Python so I wouldn’t know how to make it into a separate script.
Last thing, in my game, the script will work when the middle mouse button is held down using States. When I hold middle mouse again, the camera will snap to the mouse location. How would I make it so that the camera will not move upon holding the middle mouse button and only move if the mouse is moving?
when the camera knows there’s an object behind it using the Rays, it more or less pulses back and forth instead of getting closer to the character smoothly
Hard to say what the exact issue is and how to fix it without an example. Is the object behind the camera moving or does it have a complex collision mesh? The camera collision rays will generally work best when you only use them with static objects using simple collision shapes.
Another thing, would there be a way to take the scroll to zoom part of the script? I’m not too great with Python so I wouldn’t know how to make it into a separate script.
This easiest way is just to say the ZOOM_STEP constant near the top of the script to 0.
How would I make it so that the camera will not move upon holding the middle mouse button and only move if the mouse is moving?
I assume you mean you want to prevent the mouse from controlling the camera while the middle mouse button is being held down (but still allow the camera to move closer or further from the target if it detects collisions. If so, you can try replacing the script with this one to see if it does what you want (you shouldn’t need to use states).
# Third Person Camera - Made by Mobious
import bge
import math
import mathutils as mathu
# constants
# Feel free to tweak these.
SENSITIVITY = .003 # mouse sensitivity
S = .8 # mouse smoothing
INV_X = 1 # invert x-axis
INV_Y = 1 # invert y-axis
MAX_DIST = 25.0 # maximum distance from player
MIN_DIST = 5 # minimum distance to obstacle or player
DRCAP = .5 # distance restore speed cap
RSMOOTH = 20 # camera range smoothing
FSMOOTH = RSMOOTH / 3 # front obstruction smoothing
ZSMOOTH = 13 # zoom smoothing
LRSMOOTH = 10 # camera LR smoothing
ZOOM_STEP = 1.3 # distance to zoom on mousewheel
Z_OFFSET = 1.0 # Z offset from player
HOR_OFFSET = 0.0 # Horizontal offset factor
MIN_ANGLE = math.pi/20 # minimum angle camera can be from verticle
PROP = 'block_camera' # property that block camera
# Change 'Player' to whatever object you want the camera to track
scene = bge.logic.getCurrentScene()
player = scene.objects['Player']
# controls
ZoomIn = bge.events.WHEELUPMOUSE
ZoomOut = bge.events.WHEELDOWNMOUSE
ToggleLR = bge.events.MIDDLEMOUSE
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
mouse = cont.sensors['Mouse']
rear = cont.sensors['rear']
front = cont.sensors['front']
# initialize vars
if 'x' not in own:
own['x'] = 0.0
own['y'] = math.pi / 3
own['dist'] = MAX_DIST
x = bge.render.getWindowWidth() // 2
y = bge.render.getWindowHeight() // 2
own['size'] = (x,y)
bge.render.setMousePosition(x,y)
own['zoom'] = MAX_DIST
own['oldx'] = 0.0
own['oldy'] = 0.0
rear.range = MAX_DIST
front.range = MAX_DIST - MIN_DIST
rear.propName = PROP
front.propName = PROP
own['LR'] = HOR_OFFSET
own['LRinv'] = 1
return
# check and correct for camera obstructions using smoothing
# obstacle in front of camera
if front.positive:
dist = own.getDistanceTo(front.hitPosition)
if (own['dist'] - dist) > MIN_DIST:
own['dist'] -= (dist + MIN_DIST) / FSMOOTH
# obstacle behind camera
elif rear.positive:
dist = own.getDistanceTo(rear.hitPosition)
if dist > MIN_DIST:
if own['dist'] - own['zoom'] < 0:
own['dist'] += (dist - min(MIN_DIST, own['zoom'])) / RSMOOTH
elif own.getDistanceTo(player) > MIN_DIST:
own['dist'] -= min(MIN_DIST - dist, own['dist'] - MIN_DIST) / RSMOOTH
elif own['dist'] < own['zoom']: # restore max distance
dist = (own['zoom'] - own['dist'])
own['dist'] += dist / ZSMOOTH
if own['dist'] > own['zoom']: # zoom in
own['dist'] -= (own['dist'] - own['zoom']) / ZSMOOTH
front.range = own['dist'] - MIN_DIST
# mouse movement
keys = bge.logic.mouse.events
keys.update(bge.logic.keyboard.events)
xpos = own['size'][0]
ypos = own['size'][1]
if keys[bge.events.MIDDLEMOUSE] == bge.logic.KX_INPUT_NONE:
x = INV_X*(xpos - mouse.position[0])
y = -INV_Y*(ypos - mouse.position[1])
x *= SENSITIVITY
y *= SENSITIVITY
bge.render.setMousePosition(xpos,ypos)
elif keys[bge.events.MIDDLEMOUSE] == bge.logic.KX_INPUT_JUST_RELEASED:
bge.render.setMousePosition(xpos,ypos)
x = y = 0
else:
x = y = 0
# smooth movement
own['oldx'] = (own['oldx']*S + x*(1-S))
own['oldy'] = (own['oldy']*S + y*(1-S))
x = own['oldx']
y = own['oldy']
# zoom controls
if keys[ZoomIn] and own['zoom'] >= MIN_DIST + ZOOM_STEP:
own['zoom'] -= ZOOM_STEP
elif keys[ZoomOut] and own['zoom'] <= MAX_DIST - ZOOM_STEP:
own['zoom'] += ZOOM_STEP
# update angles
inv = False
own['x'] += x
own['y'] -= y
if own['x'] > math.pi:
own['x'] -= 2*math.pi
inv = True
elif own['x'] <= -2*math.pi:
own['x'] += 2*math.pi
inv = True
if own['y'] > math.pi - 2*MIN_ANGLE:
own['y'] = math.pi - 2*MIN_ANGLE
elif own['y'] < 0.0 + MIN_ANGLE:
own['y'] = 0.0 + MIN_ANGLE
# LR toggling and smoothing
if keys[ToggleLR] == 1:
own['LRinv'] *= -1
dist = own['LRinv']*HOR_OFFSET - own['LR']
own['LR'] += dist / LRSMOOTH
dist = own['LR'] / math.pow(own['dist'], 2)
# calculate and set new camera position
own['tdist'] = own['dist']*math.fabs(math.cos(math.pi/2 - own['y']))
zshift = own['dist']*math.sin(math.pi/2 - own['y'])
z = player.worldPosition[2] + zshift + Z_OFFSET
yshift = own['tdist']*-math.cos(own['x'])
y = player.worldPosition[1] + yshift + dist*math.sin(own['x'])
xshift = own['tdist']*math.sin(own['x'])
x = player.worldPosition[0] + xshift + dist*math.cos(own['x'])
own.worldPosition = [x,y,z]
#set new camera angle
vec = mathu.Vector((own['y'], 0, own['x']))
own.localOrientation = vec
Thank you, the script works for the most part and you’re right, I don’t need to use States which is an added bonus for me. However when the middle mouse button isn’t held down, the mouse is stuck in the centre of the screen. How would you go about making it so the cursor can be moved?
About the camera collision, I recorded the problem.
Use this script if you want the mouse to always be able to move around.
# Third Person Camera - Made by Mobious
import bge
import math
import mathutils as mathu
# constants
# Feel free to tweak these.
SENSITIVITY = .003 # mouse sensitivity
S = .8 # mouse smoothing
INV_X = 1 # invert x-axis
INV_Y = 1 # invert y-axis
MAX_DIST = 25.0 # maximum distance from player
MIN_DIST = 5 # minimum distance to obstacle or player
DRCAP = .5 # distance restore speed cap
RSMOOTH = 20 # camera range smoothing
FSMOOTH = RSMOOTH / 3 # front obstruction smoothing
ZSMOOTH = 13 # zoom smoothing
LRSMOOTH = 10 # camera LR smoothing
ZOOM_STEP = 1.3 # distance to zoom on mousewheel
Z_OFFSET = 1.0 # Z offset from player
HOR_OFFSET = 0.0 # Horizontal offset factor
MIN_ANGLE = math.pi/20 # minimum angle camera can be from verticle
PROP = 'block_camera' # property that block camera
# Change 'Player' to whatever object you want the camera to track
scene = bge.logic.getCurrentScene()
player = scene.objects['Player']
# controls
ZoomIn = bge.events.WHEELUPMOUSE
ZoomOut = bge.events.WHEELDOWNMOUSE
ToggleLR = bge.events.MIDDLEMOUSE
def main():
cont = bge.logic.getCurrentController()
own = cont.owner
mouse = cont.sensors['Mouse']
rear = cont.sensors['rear']
front = cont.sensors['front']
# initialize vars
if 'x' not in own:
own['x'] = 0.0
own['y'] = math.pi / 3
own['dist'] = MAX_DIST
x = bge.render.getWindowWidth() // 2
y = bge.render.getWindowHeight() // 2
own['size'] = (x,y)
bge.render.setMousePosition(x,y)
own['zoom'] = MAX_DIST
own['oldx'] = 0.0
own['oldy'] = 0.0
own['prevPos'] = [0, 0]
rear.range = MAX_DIST
front.range = MAX_DIST - MIN_DIST
rear.propName = PROP
front.propName = PROP
own['LR'] = HOR_OFFSET
own['LRinv'] = 1
return
# check and correct for camera obstructions using smoothing
# obstacle in front of camera
if front.positive:
dist = own.getDistanceTo(front.hitPosition)
if (own['dist'] - dist) > MIN_DIST:
own['dist'] -= (dist + MIN_DIST) / FSMOOTH
# obstacle behind camera
elif rear.positive:
dist = own.getDistanceTo(rear.hitPosition)
if dist > MIN_DIST:
if own['dist'] - own['zoom'] < 0:
own['dist'] += (dist - min(MIN_DIST, own['zoom'])) / RSMOOTH
elif own.getDistanceTo(player) > MIN_DIST:
own['dist'] -= min(MIN_DIST - dist, own['dist'] - MIN_DIST) / RSMOOTH
elif own['dist'] < own['zoom']: # restore max distance
dist = (own['zoom'] - own['dist'])
own['dist'] += dist / ZSMOOTH
if own['dist'] > own['zoom']: # zoom in
own['dist'] -= (own['dist'] - own['zoom']) / ZSMOOTH
front.range = own['dist'] - MIN_DIST
# mouse movement
keys = bge.logic.mouse.events
keys.update(bge.logic.keyboard.events)
xpos = own['prevPos'][0]
ypos = own['prevPos'][1]
own['prevPos'] = mouse.position
if keys[bge.events.MIDDLEMOUSE] == bge.logic.KX_INPUT_NONE:
x = INV_X*(xpos - mouse.position[0])
y = -INV_Y*(ypos - mouse.position[1])
x *= SENSITIVITY
y *= SENSITIVITY
#bge.render.setMousePosition(xpos,ypos)
elif keys[bge.events.MIDDLEMOUSE] == bge.logic.KX_INPUT_JUST_RELEASED:
#bge.render.setMousePosition(xpos,ypos)
x = y = 0
else:
x = y = 0
# smooth movement
own['oldx'] = (own['oldx']*S + x*(1-S))
own['oldy'] = (own['oldy']*S + y*(1-S))
x = own['oldx']
y = own['oldy']
# zoom controls
if keys[ZoomIn] and own['zoom'] >= MIN_DIST + ZOOM_STEP:
own['zoom'] -= ZOOM_STEP
elif keys[ZoomOut] and own['zoom'] <= MAX_DIST - ZOOM_STEP:
own['zoom'] += ZOOM_STEP
# update angles
inv = False
own['x'] += x
own['y'] -= y
if own['x'] > math.pi:
own['x'] -= 2*math.pi
inv = True
elif own['x'] <= -2*math.pi:
own['x'] += 2*math.pi
inv = True
if own['y'] > math.pi - 2*MIN_ANGLE:
own['y'] = math.pi - 2*MIN_ANGLE
elif own['y'] < 0.0 + MIN_ANGLE:
own['y'] = 0.0 + MIN_ANGLE
# LR toggling and smoothing
if keys[ToggleLR] == 1:
own['LRinv'] *= -1
dist = own['LRinv']*HOR_OFFSET - own['LR']
own['LR'] += dist / LRSMOOTH
dist = own['LR'] / math.pow(own['dist'], 2)
# calculate and set new camera position
own['tdist'] = own['dist']*math.fabs(math.cos(math.pi/2 - own['y']))
zshift = own['dist']*math.sin(math.pi/2 - own['y'])
z = player.worldPosition[2] + zshift + Z_OFFSET
yshift = own['tdist']*-math.cos(own['x'])
y = player.worldPosition[1] + yshift + dist*math.sin(own['x'])
xshift = own['tdist']*math.sin(own['x'])
x = player.worldPosition[0] + xshift + dist*math.cos(own['x'])
own.worldPosition = [x,y,z]
#set new camera angle
vec = mathu.Vector((own['y'], 0, own['x']))
own.localOrientation = vec
As for the collision issue, I can’t really tell what the problem is from just the video. You should try to create a simple blend file that replicates the problem and submit that.