Blender GE - "разборки" с мышью :)

Часто на форумах спрашивают, как работать с мышкой в GE?
Я нашел отличный урок, в котором автор (Andrew-101 из Австралии (Мельбурн)) популярно рассказал об этом и дал пример файла.

Если вы серьезно решили делать игру или просто хотите пропробовать свои силы в мини-игре, вам понадобится разобраться с использованием мыши.

В этом уроке я расскажу как использовать мышь в GE, получать информацию с нее и затем использовать эту информацию. В примере есть 8 сцен которые помогут вам разобраться с этим досконально, а именно:
* Как отобразить курсор мыши в GE.
* Как получить координаты курсора мыши
* Как получить координаты курсора мыши относительно центра экрана
* Перемещение камеры за курсором
* Перемещение объекта с помощью мыши
* Мгновенно переместить объект в точку экрана где вы кликнули мышкой
* Плавно переместить объект в точку нажатия мышки
* Заставить объект появится в месте нажатия мышки и передвигать эти объекты
Для начала необходимо иметь некоторые навыки Python в GE.

The Mouse Sensors

Давайте посмотрим, какие виды сенсора мыши (Mouse) предоставляет нам Blender. Их 8 - вот они:
* Left Button - Левая кнопка
* Middle Button - Средняя кнопка
* Right Button - Правая кнопка
* Wheel Up - Колесико вверх
* Wheel Down - Колесико вниз
* Movement - Перемещение
* Mouse Over - Перемещение над..
* Mouse Over Any - Перемещение над любым объектом

Первые 5 сенсоров - ничего особенного, с их помощью мы работаем с кнопками мыши, а вот с последними 3-мя стоит разобраться.

Сенсор Movement имеет две функции, использование которых поможет нам реализовать некоторые функции - эти функции .getXPosition() и .getYPosition(). Каждая из этих функций возвращает целое число, это X и Y координаты курсора мыши.
Сенсор Mouseover тоже полезен, с помощью функции isPositive() мы получаем булево значение Истина (true) если курсор мыши находится над владельцем сенсора (объект, к которому вы прикрепили сенсор). Сенсор Mouseover также имеет две функции .getXPosition() и .getYPosition() для получения координат (и множество других), однако мы будем использовать функцию .getHitPosition(). Эта функция возвращает список из x,y и z координат над которыми находится мышь.
Сенсор MouseOverAny похож на предыдущий, за исключением того, что срабатывает не только при перемещении мыши над владельцем сенсора, но и над любым другим объектом.

"Проявляем" курсор мышки в GE
Если вы планируете использовать мышь в GE тогда нам для начала надо отобразить сам курсор мышки.
Для этого нам понадобятся лишь две строчки кода:

import Rasterizer
Rasterizer.showMouse(1)

Первая строка - import Rasterizer - импортирует модуль Rasterizer. Этот модуль управляет экраном в GE, вы можете использовать строчку 'print dir(Rasterizer)', чтобы посмотреть на все функции, которые есть в этом модуле. Вторая строка 'Rasterizer.showMouse(1)' вызывает функцию '.showMouse()' из модуля с параметром 1 (1 - это также True в в python, 0 - False соответственно), говорящая что Rasterizer должен показать мышь. Если мышь не нужна - надо использовать '.showMouse(0)' или '.showMouse(False).

Получаем координаты курсора мыши.
Как их получить? В файле примера для этого откройте сцену 2 и скрипт example02. Кликните на пустышке с именем ('Control2') и в окне Logic посмотрите скрипт контроллера. Для получения координат используются две функции:
mouse_x = mouse.getXPosition()
mouse_y = mouse.getYPosition()

Координаты даются относительно верхнего левого угла.
Это очень просто, не правда ли?

Получение координат курсора относительно центра экрана.

В прошлом примере мы быстро получили координаты курсора, но начало координат находилось в левом верхнем углу. А если мы хотим чтобы начало было в центре экрана? В файле примера выбираем сцену 3, запускаем GE, останавливаем и смотрим вывод консоли - там уже то, что нам нужно. Как это сделать?
Сделать это можно с помощью 4-ех строчек кода:

window_width = Rasterizer.getWindowWidth()
window_height = Rasterizer.getWindowHeight()

mouse_x = mouse.getXPosition() - window_width/2
mouse_y = mouse.getYPosition() - window_height/2

Появились две новые переменные - window_width и window_height, и как вы догадываетесь они нужны, чтобы получить ширину и высоту экрана с помощью функций модуля Rasterizer. Получение значений для наших двух старых переменных mouse_x и mouse_y немного изменились. Чтобы объяснить, посмотрим на диграмму.


На этой диаграмме начало координат находится в левом верхнем углу, а как нам получить середину окна?
Найдем центр -Точка (320,240) ,она может быть получена так:

(Rasterizer.getWindowWidth() / 2, Rasterizer.getWindowHeight() / 2)

Если мы хотим теперь использовать эту точку как начало координат, то должны из координат вычисляемых точек отнимать координату центра окна (320,240).

то есть.

(120 - 320 , 65 - 240) = (-200, -175)
(320 - 320, 240 - 240) = (0, 0)


Надеюсь теперь понятно, почему для получения координат относительно центра мы проделывали эту процедуру.

Перемещение камеры за мышкой
Во многих случаях вам понадобится знать как вращать камеру с помощью мыши. В файле примера выбираем сцену и смотрим скрипт Example04. Чтобы посмотреть как это работает нажимаем P.

Давайте посмотрим на код. Он достаточно прост, за исключением некоторых новшеств:
mouse_x = float(mouse.getXPosition() - window_width/2)
mouse_y = float(mouse.getYPosition() - window_height/2)

Теперь мы преобразовываем результат координат в число с плавающей точкой. Зачем это понадобилось? Причину находим в следующей строке:

motion.setDRot(mouse_y/500, 0, -mouse_x/500, 0)

Этот код использует актуатор движения, прикрепленный к камере и устанавливающий вращение по оси X - делим значение координаты курсора по оси Y на 500, координату Y делаем 0-вой, а Z получаем с помощью деления X координаты курсора на 500 (если делить на 200 или 100 то вращение будет более быстрое и чувствительное).

Зачем я применял float к координатам мыши? Потому что если мы поделим целое число 40 на 500 то получим 0 а когда число с плавающей точкой то 40/500=0.08.

GameLogic.addActiveActuator(motion, 1)

Если вы не знаете зачем нужна эта строка кода, следует подучить python в GE, с его помощью мы активируем наш актуатор с установленным значением поворота камеры.
Rasterizer.setMousePosition(window_width/2,window_height/2)

Эта строка устанавливает положение курсора мыши снова в центр экрана.


Перемещаем объект с помощью мыши.

Перейдем к более интересным примерам, выбираем сцену 5 файла и смотрим скрипт Example05. Если нажмете P и запустите GE, то сможите кликом и ужерживанием на кубе перетаскивать его.

Давайте посмотрим как это сделано. Ранее я упоминал о функции getHPosition для сенсора mouseover. Мы используем эту функцию в этом примере. Функция .getHPosition() возвращает x,y и z положения над которыми находится мышь - представьте луч, который посылается из курсора мыши пока на столкнется с объектом. Так вот с помощью этой функции мы можем установить положение куба в положение курсора мыши, правильно? НЕТ!

Вспомните, что когда мышь пересекает объект , то это совсем не центр объекта, но мы делаем его центром устанавливая положение объекта в нужную точку. В этом случае наш куб будет сдвигаться в сторону камеры. Попробуйте изменить строчку:
own.setPosition([hit_pos[0], 0, hit_pos[2]])

на:

own.setPosition([hit_pos[0], hit_pos[1], hit_pos[2]])

Нажмите P и попробуйте передвинуть куб. Видите о чем я говорю? Вот почему я должен установить положение y куба в 0, чтобы он по этой координате оставался в одном месте.
Вот основной код:

if mouseOver.isPositive() & leftClick.isPositive():
hit_pos = mouseOver.getHitPosition()
own.setPosition([hit_pos[0], 0, hit_pos[2]])

Если вы не знаете что такое if надо немного подучить python. Это очень просто, эта инструкция говорит: когда срабатывает сенсор mouseover (isPositive=True) и нажата левая кнопка мыши, то положение объекта устанавливается в положение курсора мыши (а координату y объекта приравниваем к 0, чтобы объект не наезжал на камеру).

Мгновенное перемещение объекта к точке нажатия мыши

Если вы видели из прошлого примера, когда вы очень быстро перемещаете курсор то куб как бы "отваливается". Это происходит если курсор за время одного "тика" перемещается на расстояние большее размеров куба и сенсор MouseOver перестает срабатывать. Давайте посмотрим как нам это исправить. Вы знаете что делать - выбираем следующую сцену, следующий скрипт. Теперь мы используем mouseoverany сенсор и плоскость за кубом(вы может и не заметили ее сразу - повращайте вид на 180 градусов).

Теперь мы используем сенсор mouseoverany и он будет срабатывать всегда, так как мы находимся над плоскостью. Однако он сработает даже тогда, когда мы не пересекаем куб и куб моментально перейдет в точку нажатия.


Плавное перемещение объекта объекта к месту нажатия мышки.
Давайте попробуем сделать что-то получше чем мгновенное перемещение куба в точку клика, давайте будем перемещать куб плавно к точке нажатия и вслед за мышкой.

Следующая сцена, следующий скрипт. Нажимаем P и рассматриваем. До того, как посмотреть как это работает надо знать методы работы. Есть три метода в этом скрипте: getQuadrant(), getAngle() и track().

Начнем с getQuadrant():
def getQuadrant():
if own_pos[0] >= cube_pos[0]:
if own_pos[2] >= cube_pos[2]:
quadrant = 1
else:
quadrant = 4
else:
if own_pos[2] >= cube_pos[2]:
quadrant = 2
else:
quadrant = 3

return(quadrant)

Что он делает? Представим, что экран поделен на 4 части - квадранты:


Теперь представим, что центр - это положение коробки-куба. Все что делает этот метод это следит за положением пустышки (а пустышка реально находится там, где мы щелкаем мышкой) по отношению к кубу и определяет в каком квадранте она находится - 1,2,3 или 4. И когда X-координата мышки больше чем X куба и Y пустышки больше чем Y куба, то значит пустышка находится в первом квадранте, правильно? Просто посмотрите на код:
if own_pos[0] >= cube_pos[0]:
if own_pos[2] >= cube_pos[2]:
quadrant = 1

Когда метод нашел в каком квадранте находится пустышка (нажат курсор) он возвращает информацию:
return(quadrant)

Следующая функция, которую мы рассмотрим это getAngle(). Этот метод даст нам значение угла, образованного координатами куба и пустышки.
Вот код:
x = cube_pos[0]
z = own_pos[2]

pivot.setPosition([x, 0, z])

Диаграмма поможет нам лучше понять, что происходит.


Как вы видите из диаграммы, мы берем за точку pivot (центр) для вычисления угла координату X куба и координату Z пустышки, чтобы сформировать правильный прямоугольный треугольник. И надеюсь вы знаете что такое прямоугольный треугольник ;). Давайте посмотрим на вторую строку в коде:
from math import atan, degrees


Мы импортируем из модуля math нужные нам atan и degress. Degress преобразует радианы в градусы а atan нужен для нахождения угла из значения тангенса. Давайте посмотрим на наш треугольник.

The lengths of the opposite and adjacent sides can be found be getting the distance from the cube and empty to the pivot, which is what these two lines are doing:
Длину катетов мы можем найти, расчитав расстояния от точки pivot до куба и пустышка. Для этого нам нужна была точка pivot - точка вращения.
opp = pivot.getDistanceTo(own)
adj = pivot.getDistanceTo(cube)

Теперь, когда у нас есть стороны, давайте найдем угол:

x_rise = degrees(atan(opp/adj))
z_rise = 90 - x_rise

Вы может не понимаете еще зачем нужны x_rise, z_rise сейчас, но подождите и все скоро поймете. Теперь мы возвратим эти значения из функции:
return(x_rise, z_rise)

Итак, давайте посмотрим на метод track() - этот метод так называется потому что заставляет куб следить за пустышкой. Во-первых, вызывается функция getQuadrant() и сохраняется значение квадранта в переменной 'quadrant':
quadrant = getQuadrant()

Затем вызывается метод getAngle() и сохраняется результат в переменных x_rise и y_rise:
x_rise, z_rise = getAngle()

Чтобы заставить куб перемещаться за пустышкой нам понадобится актуатор движения у куба, а также надо расчитать на какое расстояние перемещать куб по оси X и Z при движении. Вот для чего нам нужны были x_rise и z_rise, если z_rise угол больше чем x_rise тогда нам нужно больше перемещаться по оси z и наоборот.
А как быстро перемещаться? Давайте переведем наши значения x_rise и z_rise в процентные. Угол может быть максимум 90 градусов, поэтому поделим углы на 90 и получим значение от 0 до 1. 1 - это 100 %, а 0 - 0%. Вот как это сделано в коде:
x_ratio = x_rise/90.0
z_ratio = z_rise/90.0

Теперь, следующая порция кода - условия if:
if quadrant == 1:
x = 1
z = 1
elif quadrant == 2:
x = -1
z = 1
elif quadrant == 3:
x = -1
z = -1
elif quadrant == 4:
x = 1
z= -1

Теперь мы используем значения полученые с помощью getQuadrant(), и решаем в каком направлении надо совершать движения по осям. Если пустышка в 1-ом квадранте, до двигаться надо в положительную сторону по осям X и Z, если во втором квадранте, то движение в отрицательную сторону по оси X и в положительную сторону по оси Z.
Помните как я сказал что мы собираемся сделать так, чтобы куб двигался постепенно приближаясь к пустышке? Мы будем использовать расстояние от куба до пустышки как фактор, влияющий на скорость, с которой куб приближается.
distance = own.getDistanceTo(cube)/5

Я поделил на 5 потому что иначе будет слишком быстро. Давайте установим параметры актуатора куба:
motion.setDLoc(x_ratio * distance * x, 0, z_ratio * distance * z, 1)
GameLogic.addActiveActuator(motion, 1)

Используется величина расчитанного фактора расстояния умноженная на фактор направления и величину перемещения по осям. Как и в прошлом примере, для оси Y мы вписали 0.

Осталось совсем немного кода, спросите зачем еще что-то? Это для пустышки:
if mouseOverAny.isPositive() & leftClick.isPositive():
hit_pos = mouseOverAny.getHitPosition()
own.setPosition([hit_pos[0], 0, hit_pos[2]])

Где то вы это уже видели? :)

Рождаем объекты в месте клика мышки

Теперь последний пример из урока. 8 сцена, скрипт example08_1. Посмотрите, чтобы был включен слой 1. Нажмите P и покликайте мышкой на экране. В местах клика будут появлятся и падать кубы. Вы можете "ухватить" правой кнопкой мыши новый куб и потаскать его.

Посмотрим как это работает. Когда вы кликаете левой кнопкой на экране то в месте клика (положении пустышки) появляется куб (который на самом деле спрятан во втором слое). Переключитесь на второй слой и откройте скрипт example08_2 - этот скрипт привязан к кубу и имеет сенсор для правой кнопки мыши, сенсор для mouseover и сенсор mouseoverany, и когда они оба активируются вы можете перетаскивать куб. Давайте посмотрим на куб. У него есть свойство (property) selected, установливающееся в 1, когда вы кликаете на нем и 0 - когда вы отпускаете правую кнопку мыши. Для этого нужен такой код:
if mouseOver.isPositive() & rightClick.isPositive():
own.selected = 1

if not rightClick.isPositive():
own.selected = 0

if own.selected == 1:
hit_pos = mouseOverAny.getHitPosition()
own.setPosition([hit_pos[0], 2, hit_pos[2]])

Все что делает этот пример перетаскивания это комбинация нашего первого примера перетаскивания и второго примера перетаскивания, вы можете двигать мышкой очень быстро, но куб не отвалится пока вы удерживаете правую кнопку мыши.


Есть еще один пример работы с мышью и с "самодельным" курсором с сайта blendenzo. О нем - в следующий раз, если это конечно кому-нибудь будет интересно :)
Blender GE - "разборки" с мышью :) Автор: Maks Zinchenko дата: 10:57 Оценка: 5

7 комментариев:

  1. "Есть еще один пример работы с мышью и с "самодельным" курсором с сайта blendenzo. О нем - в следующий раз, если это конечно кому-нибудь будет интересно :)"

    О чем вопрос, конечно интересно!
    Надеюсь на продолжение :)

    ОтветитьУдалить
  2. ссылки на пример умерли... по возможности обновите или залейте кудато еще.

    ОтветитьУдалить
  3. оеративно. но у меня редиректит на какуюто явно завирусованную страницу... по возможности можно перекинуть файлы на народ или еще какйто общедоступный ресурс

    ОтветитьУдалить
  4. если вы про файл с примерами то попробуйте http://narod.ru/disk/16353162000/mouse-theory.blend.html
    скинул туда
    но что-то странное у вас в броузере творится

    ОтветитьУдалить
  5. Спасибо за фаил... как ни странно проблема исключительно с ссылкой на пример... остальное работает нормально... полез дальше бороться с питоном... еще раз спасибо!

    ОтветитьУдалить

Все права защищены BlenderTech © 2008 - 2015
Поддержка BloggerSweetheme
Автор изображений для темы: friztin. Технологии Blogger.