Камераи шахси аввал
Чаро бозигар ва камераро ҷудо мекунем
Камера муайян мекунад, ки саҳнаро аз куҷо мебинем. Дар намуди шахси аввал нигоҳ бояд бозигарро пайравӣ кунад, аммо хам кардани сар набояд бадан ва самти роҳравиро хам кунад. Аз ин рӯ иерархия қулай аст: бозигар дар гирди меҳвари амудӣ мегардад ва камераи фарзанди он ба боло ва поён хам мешавад. Бисёр симуляторҳо ва сайругаштҳои маҷозӣ дар биноҳо ҳамин тавр кор мекунанд.
Yaw, pitch ва parent
Yaw гардиш ба чапу рост, яъне rotation_y аст. Pitch хамшавӣ ба болою поён, яъне rotation_x аст. Дар барномаи равзанадор camera объекти тайёри камераи Ursina мебошад. camera.parent = player ва фосилаи маҳаллии баландии чашмро таъин мекунанд. Дар мисол Entity бо номи view камераро иваз мекунад, то робитаро бе равзана санҷем.
from ursina import Entity
player = Entity(position=(2, 0, 3))
view = Entity(parent=player, y=1.6)
player.z += 2
view.rotation_x = 25
print(view.world_position)
print(round(player.rotation_x), round(view.rotation_x))
Натиҷа:
Vec3(2, 1.6, 5)
0 25
Хам кардани фарзанд волидайнро хам намекунад. Агар худи player-ро хам кунед, forward-и он ҷузъи амудӣ мегирад ва роҳравӣ ба парвоз монанд мешавад. Барои роҳравӣ бадан одатан танҳо дар гирди y мегардад.
Маҳдуд кардани хамшавӣ
rotation_x-и мусбат нигоҳро ба поён ва манфӣ ба боло мебарад. clamp(value, low, high) ададро дар ҳудуд нигоҳ медорад: қимати аз ҳад калонро бо ҳади боло ва аз ҳад хурдро бо ҳади поён иваз мекунад. Pitch-ро аксар вақт ба [−90, 90] маҳдуд мекунанд, то тасвир чаппа нашавад.
from ursina import clamp
for pitch in (-120, -30, 45, 110):
print(clamp(pitch, -90, 90))
Натиҷа:
-90
-30
45
90
Ҳаракати муш
mouse.locked = True нишондиҳандаро дар маркази равзана нигоҳ дошта, пинҳон мекунад; нигоҳ ба канори равзана маҳдуд намешавад. mouse.velocity ҳаракати нисбии мушро дар кадр дорад. Ин аллакай ҷойивазкунӣ аст, бинобар ин дар тарҳи курс онро ба ҳассосият зарб мекунанд, бе time.dt-и иловагӣ. Суръати роҳравӣ бошад, time.dt мехоҳад.
mdx, mdy, sens = 0.2, 0.1, 40
print(mdx * sens)
print(-mdy * sens)
Натиҷа:
8.0
-4.0
Адади якум афзоиши yaw ва дуюм афзоиши pitch аст. Аломати минус ҳаракати мушро ба боло ба нигоҳи боло табдил медиҳад. Ҳассосият миқёси ҳаракат аст; беҳтар аст онро танзими алоҳида нигоҳ доред, на дар ҳар сатр такрор кунед.
Ба пеш нисбат ба нигоҳ
Вектори ҷаҳонии (0, 0, 1) ҳамеша дар самти z аст. forward-и маҳаллии Entity ҳангоми гардиш иваз мешавад; right ба тарафи рости он ишора мекунад. Бо ин векторҳо нисбат ба бадан ҳаракат кардан мумкин аст. Дар санҷиш танҳо yaw-ро гирифта, қиматҳоро гирд мекунем: ҳисобҳои гардиш метавонанд хатои хеле хурди ададӣ дошта бошанд.
from panda3d.core import loadPrcFileData
loadPrcFileData("", "coordinate-system y-up-left")
from ursina import Entity
body = Entity(rotation_y=90)
print(tuple(round(v, 2) + 0.0 for v in body.forward))
print(tuple(round(v, 2) + 0.0 for v in body.right))
Натиҷа:
(1.0, 0.0, 0.0)
(0.0, 0.0, -1.0)
Ду сатри аввал системаи меҳварҳои Panda3D-ро барои мисоли алоҳидаи бе равзана интихоб мекунанд. Дар оғози маъмулии Ursina() ин танзим худкор аст. Самтҳои математикӣ бояд дар ҳамон системаи меҳварҳои бозӣ ҳисоб шаванд.
Дар ҳисоби дастӣ sin ва cos-и yaw истифода мешаванд, аммо math.sin ва math.cos радиан мегиранд. Аввал дараҷаҳоро бо math.radians табдил диҳед. Ҷамъи самтҳои пеш ва рост дарозиаш аз 1 зиёд аст; нормализатсия тезшавии диагоналиро бартараф мекунад.
import math
from ursina import Vec3
r = math.radians(30)
print(f"{math.sin(r):.3f} {math.cos(r):.3f}")
diagonal = Vec3(1, 0, 1)
print(f"{diagonal.length():.3f}")
print(f"{diagonal.normalized().length():.3f}")
Натиҷа:
0.500 0.866
1.414
1.000
Майдони дид ва контроллери тайёр
Майдони диди душман секторест бо самти марказӣ ва паҳнои пурраи fov. Агар fov=80 бошад, ба ҳар тарафи марказ 40° рост меояд. Санҷиши дурӣ кофӣ нест: бозигари наздик метавонад пушти сар бошад. Фосилаи кунҷиро бо нисфи fov муқоиса карда, гузариш аз 0°-ро ба ҳисоб мегиранд. Барои деворҳо боз санҷиши шуоъ лозим аст: танҳо сектор намоён будани паси монеаро муайян намекунад.
fov = 80
half = fov / 2
print(half)
for separation in (15, 55):
print(separation <= half)
Натиҷа:
40.0
True
False
Дар бозӣ тартиб чунин аст: муш хонда мешавад, yaw-и бадан ва pitch-и камера нав мешаванд, pitch маҳдуд мегардад ва баъд самти роҳравӣ аз бадан гирифта мешавад. Камера ҳангоми ҳисоби табдили ҷаҳонӣ мавқеъ ва гардиши волидайнро мерос мегирад. Барои бозии тайёр FirstPersonController-ро аз ursina.prefabs.first_person_controller ворид мекунанд: он муш, роҳравӣ, ҷаҳиш ва ҷозибаро муттаҳид мекунад. Онро баъди Ursina() созед ва ба фаршу деворҳо collider="box" диҳед.
Хатоҳои маъмул ва ислоҳи онҳо
- player.rotation_x = pitch баданро хам карда, ба роҳравӣ ҷузъи амудӣ медиҳад. Камераро хам кунед ва yaw-ро дар бозигар нигоҳ доред.
- pitch += mdy * sens ҳангоми ҳаракати муш ба боло ба поён менигарад. Барои қоидаи курс аломати муқобилро истифода баред.
- math.sin(yaw) бо yaw-и дараҷавӣ самти нодуруст медиҳад. Аввал math.radians-ро хонед.
- forward + right бе нормализатсия ҳаракати диагоналиро тез мекунад. Самти ғайрисифриро ба дарозии 1 оред.
- separation <= fov секторро ду баробар васеъ мекунад. Бо fov / 2 муқоиса кунед.
Ёддошт
- camera.parent = player; баландии чашм фосилаи маҳаллӣ аст.
- Yaw — rotation_y-и бозигар; pitch — rotation_x-и камера.
- mouse.locked барои нигоҳи озод ва mouse.velocity барои ҳаракати нисбӣ аст.
- clamp pitch-ро маҳдуд мекунад; % 360 yaw-ро даврӣ мекунад.
- forward ва right самтҳои бадананд; тригонометрия радиан мегирад.
- Майдони дид дурӣ ва нисфи fov-ро месанҷад; деворҳо санҷиши ҷудогона мехоҳанд.
- FirstPersonController ба collider-и фарш ва деворҳо ниёз дорад.