Главная » Статьи » Деццкий сад

Урок 4. Немного коллизий.
Сегодня, как и обещал, мы немного поковыряем пандовские коллизии. Задача — сделать так чтобы наша камера перемещалась по поверхности террайна и не проваливалась в него. Открываем control.py и импортируем нужные для работы модули:
Code

from pandac.PandaModules import CollisionTraverser,CollisionNode
from pandac.PandaModules import CollisionHandlerQueue,CollisionRay, BitMask32

Теперь напишем в нём вспомогательный класс:

Code

class heightChecker():
  def __init__(self):
  self.picker = CollisionTraverser()  
  self.pickerQ = CollisionHandlerQueue()  
  pickerCollN = CollisionNode('heightChecker')  
  self.pickerNode = render.attachNewNode(pickerCollN)  
  pickerCollN.setFromCollideMask(BitMask32.bit(1))  
  pickerCollN.setIn toCo llid eMask(BitMask32.allOff())  
  self.pickerRay = CollisionRay(0,0,300,0,0,-1)  
  pickerCollN.addSolid(self.pickerRay)  
  self.picker.addCollide r(se lf.p ickerNode, self.pickerQ)
  def getHeight(self,obj,pos):
  res=0
  self.pickerNode.setPos(pos)
  self.picker.traverse(obj)
  if self.pickerQ.getNumEntries() > 0:
  self.pickerQ.sortEntries()
  res=self.pickerQ.getEntry(0).getSurfacePoint(render).getZ()
  return res

Процедура инициализации нашего класса:
Первая строка — здесь мы создаём объект — экземпляр класса CollisionTraverser, который собственно и просчитывает все наши столкновения, о прежде чем он сможет это всё считать, ему нужно сообщить чего, собственно, считать-то нужно. Этим мы и занимаемся в процедуре инициализации
Вторая строка — эта хреновина будет записывать столкновения, которые обнаружил CollisionTraverser. Замечу, что вообще существует несколько типов хэндлеров, каждый из которых ведёт себя по-своему:
CollisionHandlerQueue — это очередь, про которую я только что говорил
CollisionHandlerEvent — эта штука не записывает столкновения, а генерирует на их основе события
CollisionHandlerPusher — отталкивает, не даёт входить одному объекту в другой
PhysicsCollisionHandler — пушер с некоторыми элементами физики
CollisionHandlerFloor — симуляция пола + опять же элементы физики
Тем, кто услышал слово физика и сразу заинтересовался — моё ИМХО — оно того не стоит — встроенная пандовская физика достаточно примитивна, хотя х.з. - может кому и пригодится.
Я выбрал первый вариант, т. к. он наиболее наглядный и даёт наибольший контроль (наравне с CollisionHandlerEvent)
Третья строка — просто создаём новый узел, однако не простой а золо... ээ умеющий коллидиться, ну это думаю и так понятно.
Чевёртая — прикрепляем его к корневому узлу сцены.
Пятая и шестая строки — задаётся маска коллизий. Эта самая маска определяет какие с какими объектами будут коллидиться. В простом случае коллизия происходит, если у объектов одинаковая маска и не происходит — если разная. SetFrom — определяет с кем может столкнуться наш узел. А SetInto — определяет кто может столкнуться с нашим узлом (в нашем случае никто — allOff). Кажется бессмысленная фраза, однако, если представить каким образом рассчитывается столкновение, то всё становится на свои места. В грубом приближении — возьмём три узла. При проверке сначала берётся первый и проверяется на столкновение с другими двумя, затем второй с первым и третьим и т. д. - так вот, узел который берётся — это Fom, а узел с которым проверяется столкновение — это Into.
Седьмая — создаётся тело, которое будет сталкиваться — в нашем случае это луч. Первая тройка цифр в параметре — это стартовая точка луча, вторая — направление. У нас он начинается в трёхстах единицах вверх по оси Z и направлен вниз.
Восьмой строкой мы добавляем наше созданное тело (луч) к коллидящемуся узлу.
Ну и последней строкой сообщаем траверсеру, что у нас есть некоторый узел, (с) которым мы будем проверять столкновения и есть обработчик (в нашем случае CollisionHandlerQueue) который будет рулить результатами проверки.
Небольшое пояснение — в траверсер необходимо передавать только активные объекты, т. е. те, которые сами могут детектить коллизии, для всех остальных (например для нашего террайна) достаточно просто установить маску, хотя есть и другие способы, которые применяются чаще, чем установка маски напрямую, но о них в другой раз.
И ещё одно отступление — вместе с DirectStart импортируется base.cTrav — дефолтный траверсер, он проверяет коллизии автоматом без специального вызова метода traverse(), так что во многих примерах вы можете увидеть именно его.
Вернёмся к коду.
Смотрим функцию getHeight:
Этой функцией мы будем проверять высоту нашего террайна в координатах корневого узла сцены.
В качестве параметров передаём узел от которого проверять коллизии (можно проверять не всю сцену, а определённый узел и его чайлдов, для этого и указываем узел) и позицию под которой проверять высоту.
Первая строка — присваиваем 0 по дефолту, чтоб в случае если коллизий не обнаружилось не выдать результатом пустое значение.
Вторая — устанавливаем pickerNode в переданную позицию.
Тртья — запускаем проверку. Как я сказал выше — мы не используем встроенный cTrav, поэтому можем запустить проверку в том месте, в котором хотим вручную.
Четвёртая — если в результате проверки в наш обработчик записались какие-то коллизии (кол-во больше 0), то выполняем пятую и шестую строку.
Пятой строкой мы сортируем записанные коллизии, так что первой будет ближайшая к источнику.
Шестой — получаем координату Z первой коллизии.
Седьмая — возвращаем результат.

С heightChecker закончили.
Теперь добавим созданный нами класс в процедуру инициализации cameraHandler

Code

self.hc=heightChecker()

А в процедуре dragTask перед строкой vDir=Vec3(self.j3.getPos(.......
впишем следующее:
Code

  self.j1.setZ(self.hc.getHeight(render,self.j1.getPos()))
  deltaZ=self.j3.getZ(render)-(self.hc.getHeight(render,self .j 3. getPos(render))+5)
  if deltaZ<0:
  self.turnCamera(0,-deltaZ)

Здесь мы устанавливаем j1 на высоту, которую получаем с помощью нашего хейтчекера под j1, затем с помощью того же чекера считаем высоту j3 над террайном. Учитываем небольшую поправку в 5 единиц — для того чтобы камера не лежала на террайне, а находилась на небольшой высоте над ним.
Если результат рассчёта у нас меньше 0 — т. е. j3 ниже 5 единиц над поверхностью террайна, то просто поворачиваем систему (turnCamera) в вертикальной плоскости — т. е. поднимаем камеру.

Открываем location.py
В конец процедуры loadTerrain доисываем

Code

  gnodes=self.terrain.getRoot().findAllMatches("+GeomNode")
  for gnode in gnodes:
  gnode.node().setIntoCollideMask(BitMask32.bit(1))

Это то, о чём я говорил — устанавливаем маску для пассивной геометрии. Первой строкой мы получаем всю геометрию, входящую в наш террайн, затем проходим в цикле по результату и для каждого найденного геометрического объекта устанавливаем маску.
Да, и не забываем импортировать модуль from pandac.PandaModules import BitMask32

Всё. Можно запускать. Теперь наша камера должна ездить в соответствии с рельефом и не должна проваливаться ниже.

Категория: Деццкий сад | Добавил: ninth (03.08.2009)
Просмотров: 5646 | Комментарии: 6 | Рейтинг: 5.0/1
Всего комментариев: 6
1  
Код не исполняется:
1. Нет отступов (так важных для Питона) начиная со места:
def __init__(self):
self.picker = CollisionTraverser()

2. Есть строки с пробелами через каждые две буквы, пример:
pickerCollN.setIn toCo llid eMask(BitMask32.allOff())

2  
это уже приколы обработки вставляемого текста движком юкозовской цмски. А вообще я код приводил не для копирования, а для пояснения ) Даже если бы всё нормально копировалось, то ничего не запустилось бы, т.к. надо начинать с первого урока - здесь код только апгрейдится.

3  
Понятно.

4  
D:\Rawieo\dev\python\BattleTime - Snowday>D:\Panda3D\python\python.exe -E main.py
DirectStart: Starting the game.
Known pipe types:
wglGraphicsPipe
(all display modules loaded.)
:grutil(warning): Rescaling heightfield image resource/textures/heightfield.png
from 256x256 to 257x257 pixels.
Traceback (most recent call last):
File "D:\Rawieo\dev\python\BattleTime - Snowday\modules\control.py", line 61,
in dragTask
self.j1.setZ(self.hc.getHeight(render,self.j1.getPos()))
AttributeError: cameraHandler instance has no attribute 'hc'
:task(error): Exception occurred in PythonTask dragTask
Traceback (most recent call last):
File "main.py", line 11, in <module>
run()
File "D:\Panda3D\direct\showbase\ShowBase.py", line 2910, in run
self.taskMgr.run()
File "D:\Panda3D\direct\task\Task.py", line 502, in run
self.step()
File "D:\Panda3D\direct\task\Task.py", line 460, in step
self.mgr.poll()
File "D:\Rawieo\dev\python\BattleTime - Snowday\modules\control.py", line 61,
in dragTask
self.j1.setZ(self.hc.getHeight(render,self.j1.getPos()))
AttributeError: cameraHandler instance has no attribute 'hc'
Для продолжения нажмите любую клавишу . . .
Ты явно в чем-то ошибся.

5  
Текст статьи и кода нуждаются в значительном улучшении форматирования. А там нормально. Рабочий вариант.

0
6  
Можно узнать с кем вы разговариваете и о чем smile

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Онлайн всего: 1
Гостей: 1
Пользователей: 0