Главная » Статьи » Учебник: практика

Tut-Chessboard rus
# -*- coding: cp1251 -*-
# Автор: Чжан Шао и Фил Зальцман
# Модель: Эдди Ханаан
# Последнее обновление: 5/1/2005
# Перевод: Константин "esniper" Загрядский, 8.11.2009,
# kzagradskiy[kz]gmail.com, [kz] -- "собака"
#
# В этом туториале показано, как определить, на какие объекты указывает мышь
# Мы сделаем это с помощью луча столкновения (collision ray), который исходит из позиции мыши
# и указывает прямиком на сцену, и посмотрим, с чем он сталкивается. Мы выберем
# объект с ближайшим столкновением

import direct.directbase.DirectStart
from pandac.PandaModules import CollisionTraverser,CollisionNode
from pandac.PandaModules import CollisionHandlerQueue,CollisionRay
from pandac.PandaModules import AmbientLight,DirectionalLight,LightAttrib
from pandac.PandaModules import TextNode
from pandac.PandaModules import Point3,Vec3,Vec4,BitMask32
from direct.gui.OnscreenText import OnscreenText
from direct.showbase.DirectObject import DirectObject
from direct.task.Task import Task
import sys

# Сначала мы определим составляющие для цветов
BLACK = Vec4(0,0,0,1)
WHITE = Vec4(1,1,1,1)
HIGHLIGHT = Vec4(0,1,1,1)
PIECEBLACK = Vec4(.15, .15, .15, 1)

# Теперь мы определим некоторые вспомогательные функции, которые будет нам необходимы позднее

# Эта функция, получив линию (вектор плюс точка приложения) и желаемое значение Z,
# вернет нам точку на линии с желаемым значением Z -- что нам и нужно.
# Таким образом мы узнаем, где положение объекта в 3D-пространстве на основе 2D-позиции
# мыши. Он также предполагает, что мы перетаскиваем в плоскости XY.
#
# Здесь выведем из математической плоскости, решение для данной точки
def PointAtZ(z, point, vec):
return point + vec * ((z-point.getZ()) / vec.getZ())

# Удобная маленькая функция для получения надлежащей позиции для данного квадрата
def SquarePos(i):
return Point3((i%8) - 3.5, int(i/8) - 3.5, 0)

# Вспомогательная функция для определения того, квадрат должен быть белым или черным
# Модуль операций (%) генерирует каждую клетку шахматной доски
def SquareColor(i):
if (i + ((i/8)%2))%2: return BLACK
else: return WHITE

class World(DirectObject):
def __init__(self):
# Этот код ставит стандартный заголовок и текст инструкции на экране
self.title = OnscreenText(text="Panda3D: Tutorial - Mouse Picking",
style=1, fg=(1,1,1,1),
pos=(0.8,-0.95), scale = .07)
self.escapeEvent = OnscreenText(
text="ESC: Quit",
style=1, fg=(1,1,1,1), pos=(-1.3, 0.95),
align=TextNode.ALeft, scale = .05)
self.mouse1Event = OnscreenText(
text="Left-click and drag: Pick up and drag piece",
style=1, fg=(1,1,1,1), pos=(-1.3, 0.90),
align=TextNode.ALeft, scale = .05)

self.accept('escape', sys.exit) # Escape завершает
base.disableMouse() # Отключить управление мышью камерой
camera.setPosHpr(0, -13.75, 6, 0, -25, 0) # Установить камеру
self.setupLights() # Установка освещения по умолчанию

# Поскольку мы используем обнаружение столкновений для выбор (объекта), мы установили его, как
# любую другую систему обнаружения столкновений с траверсером и хандлером
self.picker = CollisionTraverser() # Создать траверсер
self.pq = CollisionHandlerQueue() # Создать хандлер
# Сделать узел столкновений для нашего выбирающего луча
self.pickerNode = CollisionNode('mouseRay')
# Прикрепить этот узел на камеру так как луч необходимо будет позиционировать
# по отношению к ней
self.pickerNP = camera.attachNewNode(self.pickerNode)
# Все, что может быть выбраным, будет использовать бит 1. Таким образом, если мы совершим другие
# столкновения мы можем выделить его.
self.pickerNode.setFromCollideMask(BitMask32.bit(1))
self.pickerRay = CollisionRay() # Создадим наш луч
self.pickerNode.addSolid(self.pickerRay) # Добавим его в узел столкновений
# Зарегистрируем луча как нечто, что может привести к столкновениям
self.picker.addCollider(self.pickerNP, self.pq)
#self.picker.showCollisions(render)

# Теперь мы создаем шахматную доску и его части

# Мы присоединим (attach) все квадраты в свой собственный корень (root). Таким образом, мы можем сделать
# проход по столкновениям только на квадратах и сэкономим время для проверки остальной
# сцены
self.squareRoot = render.attachNewNode("squareRoot")

# Для каждого квадрата
self.squares = [None for i in range(64)]
self.pieces = [None for i in range(64)]
for i in range(64):
# Загрузить, выбрать родителя, цвет и положение модели (один квадратный полигон)
self.squares[i] = loader.loadModelCopy("models/square")
self.squares[i].reparentTo(self.squareRoot)
self.squares[i].setPos(SquarePos(i))
self.squares[i].setColor(SquareColor(i))
# Установить для модели свойство сталкиваться с лучом. Если эта модель была бы
# немного более сложной, чем один полигон, вы должны были, вместо этого,
# создать сферу столкновения вокруг модели. Но для одного полигона это работает прекрасно.
self.squares[i].find("**/polygon").node().setIntoCollideMask(
BitMask32.bit(1))
# Установим тег на узле квадрата, чтобы мы могли посмотреть, что этот квадрат
# позднее в течение прохода столкновений
self.squares[i].find("**/polygon").node().setTag('square', str(i))

# Мы будем использовать эту переменную как указатель на все части в настоящее время
# в этом квадрате

# Порядок фигур на шахматной доске для белых. Этот список
# содержит функции конструкторов для классов фигур, определяемых ниже
pieceOrder = (Rook, Knight, Bishop, Queen, King, Bishop, Knight, Rook)

for i in range (8,16):
# Загрузим белые пешки
self.pieces[i] = Pawn(i, WHITE)
for i in range (48,56):
# Загрузим черные пешки
self.pieces[i] = Pawn(i, PIECEBLACK)
for i in range(8):
# Загрузить специальные фигуры для переднего ряда и выкрасить их белым
self.pieces[i] = pieceOrder[i](i, WHITE)
# Загрузить специальные фигуры для заднего ряда и выкрасить их черным
self.pieces[i+56] = pieceOrder[i](i+56, PIECEBLACK)

# Эта переменная будет представлять индекс текущего выделенного квадрата
self.hiSq = False
# Эта переменная будет представлять индекс квадрата, откуда была взята
# перетаскиваемая фигура
self.dragging = False

# Запустить задачу, которая обрабатывает выбор
self.mouseTask = taskMgr.add(self.mouseTask, 'mouseTask')
self.accept("mouse1", self.grabPiece) #left-click grabs a piece
self.accept("mouse1-up", self.releasePiece) #releasing places it

# Эта функция меняет позиции двух фигур
def swapPieces(self, fr, to):
temp = self.pieces[fr]
self.pieces[fr] = self.pieces[to]
self.pieces[to] = temp
if self.pieces[fr]:
self.pieces[fr].square = fr
self.pieces[fr].obj.setPos(SquarePos(fr))
if self.pieces[to]:
self.pieces[to].square = to
self.pieces[to].obj.setPos(SquarePos(to))

def mouseTask(self, task):
# Эта задача работает с выделением и перетаскиванием основанным на мыши

# Во-первых, очистить текущее выделение
if self.hiSq is not False:
self.squares[self.hiSq].setColor(SquareColor(self.hiSq))
self.hiSq = False

# Проверить, сможем мы получить доступ к мыши. Это нужно для того, чтобы сделать всё остальное
if base.mouseWatcherNode.hasMouse():
# получить позицию мыши
mpos = base.mouseWatcherNode.getMouse()

# Установить позицию луча на основе позиции мыши
self.pickerRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())

# Если мы что-то перетаскиваем, установить положение объекта
# в соответствующую точку на плоскости доски
if self.dragging is not False:
# Берем точку описываемую pickerRay.getOrigin (), которая относится к
# камере, относится вместо этого к рендеру (render)
nearPoint = render.getRelativePoint(camera, self.pickerRay.getOrigin())
# То же самое с направлением луча
nearVec = render.getRelativeVector(camera, self.pickerRay.getDirection())
self.pieces[self.dragging].obj.setPos(
PointAtZ(.5, nearPoint, nearVec))

# Делаем текущий проход столкновений (Делаем его только на квадратах
# в целях эффективности)
self.picker.traverse(self.squareRoot)
if self.pq.getNumEntries() > 0:
# если у нас есть какое-либо столкновение, отсортируем столкновения так что самое близкое
# будет первым, и выделим этот узел
self.pq.sortEntries()
i = int(self.pq.getEntry(0).getIntoNode().getTag('square'))
# Установим выделение на выбраном квадрате
self.squares[i].setColor(HIGHLIGHT)
self.hiSq = i

return Task.cont

def grabPiece(self):
# Если квадрат выделен и на нем фигура, установитм его в режим перетаскивания
if (self.hiSq is not False and
self.pieces[self.hiSq]):
self.dragging = self.hiSq
self.hiSq = False

def releasePiece(self):
# Отпустим фигуру. Если мы не на квадрате, вернём её в исходную
# позицию. В противном случае, меняем местами с фигшурой в новом квадрат
if self.dragging is not False: #Make sure we really are dragging something
# Мы отпускаем фигуру, но мы не на квадрате
if self.hiSq is False:
self.pieces[self.dragging].obj.setPos(
SquarePos(self.dragging))
else:
# В противном случае, поменять фигуры
self.swapPieces(self.dragging, self.hiSq)

# Мы больше не перетаскиваем ничего
self.dragging = False

def setupLights(self): # Эта функция устанавливает некоторое освещение по умолчанию
lAttrib = LightAttrib.makeAllOff()
ambientLight = AmbientLight( "ambientLight" )
ambientLight.setColor( Vec4(.8, .8, .8, 1) )
lAttrib = lAttrib.addLight( ambientLight )
directionalLight = DirectionalLight( "directionalLight" )
directionalLight.setDirection( Vec3( 0, 45, -45 ) )
directionalLight.setColor( Vec4( 0.2, 0.2, 0.2, 1 ) )
lAttrib = lAttrib.addLight( directionalLight )
render.attachNewNode( directionalLight.upcastToPandaNode() )
render.attachNewNode( ambientLight.upcastToPandaNode() )
render.node().setAttrib( lAttrib )

# Класс для фигуры. Здесь просто выполняется загрузка модели и установка первоначальной
# позиции и цвета
class Piece:
def __init__(self, square, color):
self.obj = loader.loadModelCopy(self.model)
self.obj.reparentTo(render)
self.obj.setColor(color)
self.obj.setPos(SquarePos(square))

# Классы для каждого типа фигур
# Очевидно, что мы могли бы сделать это, просто дописав строку в инициализации класса Piece.
# Но если вы пожелаете устанавить правила, как двигаться фигурам, хорошее решением для начала
# было бы сделать метод isValidMove(toSquare) для каждого типа фигур,
# а затем проверять, во время ReleasePiece, приемлем ли квадрат назначения
class Pawn(Piece):
model = "models/pawn"
class King(Piece):
model = "models/king"
class Queen(Piece):
model = "models/queen"
class Bishop(Piece):
model = "models/bishop"
class Knight(Piece):
model = "models/knight"
class Rook(Piece):
model = "models/rook"

# Провести основную инициализацию и запустить 3D-рендеринг
w = World()
run()

Источник: http://block32.site88.net/index.php?option=com_content&view=article&id=49:tut-chessboardrupy-&catid=39:tutor&Itemid=62

Категория: Учебник: практика | Добавил: ninth (15.11.2009) | Автор: esniper
Просмотров: 3605 | Рейтинг: 0.0/0
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Онлайн всего: 1
Гостей: 1
Пользователей: 0