Создание шутер от первого лица

Ребят подскажите. Надо что бы камера не летала , не проваливалась,…

from panda3d.core import loadPrcFileData

confVars = ‘’’
window-title Моя деревня
‘’’

from direct.showbase.ShowBase import ShowBase
from panda3d.core import CollisionTraverser, CollisionHandlerPusher
from panda3d.core import CollisionNode, CollisionSphere, CollisionInvSphere

import panda3d.core as p3d
p3d.load_prc_file_data(’’, ‘’‘win-size 800 600
multisamples 1
show-frame-rate-meter 1
sync-video 0’’’)

import configparser

FT_MIPMAP = p3d.SamplerState.FT_linear_mipmap_linear
FT_LINEAR = p3d.SamplerState.FT_linear
TS_NORMAL = p3d.TextureStage.M_normal
TS_NORMAL_GLOSS = p3d.TextureStage.M_normal_gloss
TS_MODULATE = p3d.TextureStage.M_modulate
F_SRGB = p3d.Texture.F_srgb
F_SRGBA = p3d.Texture.F_srgb_alpha
F_RGB = p3d.Texture.F_rgb
F_RGBA = p3d.Texture.F_rgba
M_MSAA= p3d.AntialiasAttrib.M_multisample

“”“direct - это часть Panda3D, написанная на Python core - это C++ с привязками Python)
ShowBase-это одноэлементный класс, который настраивает окно, диспетчер задач и все
другие общие вещи, которые вы можете (или не можете) использовать.
Вам не нужно его использовать, но вы, вероятно, захотите использовать или использовать его, даже если вы этого не хотите.
Он также устанавливает себя и кучу других вещей в здания, так что
ключевые объекты (например, узел рендерин доступны в любом месте вашего код”""

Класс контроллера мыши и клавы

class Controller():
# Конструктор
def init(self):

	#self.cTrav = CollisionTraverser()
	#self.pusher = CollisionHandlerPusher()
	# Значение шага перемещения клавы
	self.key_step = 0.1
	# Значение шага поворота мыши
	self.mouse_step = 0.2
	# Координаты ценра экрана
	self.x_center = base.win.getXSize()//2
	self.y_center = base.win.getYSize()//2
	# Перемещаем указатель мыши в цетре экрана
	base.win.movePointer(0, self.x_center, self.y_center)
	# Отключаем стандарное управление мышкой
	base.disableMouse()
	# Устанавливает поле зрения обьекта
	base.camLens.setFov(30)
	
	
	# Устанавливает текущее значение ориентациии камеры
	self.heading = 0
	self.pitch = 0
	
	# Запускаем задачу контроля камеры
	taskMgr.doMethodLater(0.05, self.controlCamera, "camera-task")
	
	# Регистрируем на нажатие клавиши "Esc"
	# Событие закрытия приложения
	base.accept("escape", base.userExit)
	
	# Устанавливаем клавиши управления перемещения камеры
	# Словарь хранящий флаги нажатия клавиш
	self.keys = dict()
	
	# Заполняем словарь
	for key in ['a', 'd', 'w', 's', 'e', 'q']:
		# Создаем запись в словаре
		self.keys[key] = 0
		# Регестрируем событие на нажатие клавиш
		base.accept(key, self.setKey, [key, 1])
		# Регестрируем событие на отжатие клавиш
		base.accept(key+'-up', self.setKey, [key, 0])

# Метод установки состояния клавиш
def setKey(self, key, value):
	self.keys[key] = value
	
# Метод управления положением и ориентации камеры
def controlCamera(self, task):
	# Расчитывем управлением и ориентации камерры
	move_x = self.key_step * (self.keys['d'] - self.keys['a'])
	# Добавьте перемещение камеры вперед, назад, вверх, вниз
	move_y = self.key_step * (self.keys['w'] - self.keys['s'])
	move_z = self.key_step * (self.keys['e'] - self.keys['q'])
		
	# Смещаем позицию камеры относительно предыдущего положения камеры
	base.camera.setPos(base.camera, move_x, move_y, move_z)
	
	# Получаем новое положение курсора мыши
	new_mouse_pos = base.win.getPointer(0)
	new_x = new_mouse_pos.getX()
	new_y = new_mouse_pos.getY()
	# Пробуем установить курсор в цетре экрана
	if base.win.movePointer(0, self.x_center, self.y_center):
		# Расчитайте поворот камеры по горизонтали
		self.heading = self.heading - (new_x - self.x_center) * self.mouse_step
		# Расчитайте поворот камеры по диагонали
		self.pitch = self.pitch - (new_y - self.y_center) * self.mouse_step
		# Устанавливаем новую ориентацию камеры
		base.camera.setHpr(self.heading, self.pitch, 0)
			
	# Сообщаем о необходимости повторного запуска камеры
	return task.again

if name == ‘main’:

class App(ShowBase):
	def __init__(self):
		
		super().__init__()
		
		
		self.cTrav = CollisionTraverser()
    
		pusher = CollisionHandlerPusher()
		pusher.setHorizontal(True)
		
		self.env = loader.loadModel("models/world")
		self.env.reparent_to(self.render)
		self.env.setScale(0.25, 0.25, 0.25)
		self.env.setPos(0, 0, -2)
		
		envBounds = self.env.getBounds()
		envCenter = envBounds.getCenter()
		envRad = envBounds.getRadius() * 0.8
		
		

		cNode = CollisionNode("models/world")
		cNode.addSolid(CollisionInvSphere(envCenter, envRad))
		envC = self.render.attachNewNode(cNode)
    
		camBounds = self.camera.getBounds()
		camCenter = camBounds.getCenter()
		camRad = 1
     
		cNode = CollisionNode("camera")
		cNode.addSolid(CollisionSphere(camCenter, camRad))
		camC = self.camera.attachNewNode(cNode)
		camC.show()
    
		self.cTrav.addCollider(camC, pusher)
		pusher.addCollider(camC, self.camera)



		self.controller = Controller()

app=App()
app.run()

Что значит не летала и не проваливалась? Если имелось ввиду чтобы камера на 360 градусов по вертикали не крутилась то можно pitch ограничить от -90 до 90, как обычно в шутерах делают.
Очень странное решение камеру обновлять каждые 20мс. По идее нужно обновлять камеру вообще каждый кадр а не лочить на 50фпс иначе не будет плавности.

Нет колизий и маски на камере. Летает по небу. Проваливается в пол )

@Denis_Malinov рекомендую всё-таки общатся по английски. Это привлечёт больше людей к вашей проблеме. Не всем хочется долго переводить с русского. Также для решения проблем рекомендую вам Scripting Issues - Panda3D.