Сегодня, как и обещал, мы немного поковыряем пандовские коллизии. Задача — сделать так чтобы наша камера перемещалась по поверхности террайна и не проваливалась в него. Открываем 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 Всё. Можно запускать. Теперь наша камера должна ездить в соответствии с рельефом и не должна проваливаться ниже.
|