In this chapter, you’ll modify your rendering engine and begin architecting your game engine by abstracting rendering code away from scene management. Game engines can include features such as physics engines and sound. The game engine you’ll produce in this chapter doesn’t have any high-end features, but it’ll help you understand how to integrate other components as well as give you the foundation needed to add complexity later.
For this exercise, you’ll create a scene with a drivable car and a controllable player. This should whet your appetite and provide inspiration for making your own games, which is, after all, what this book is all about!
Scenes
A scene can consist of one or more cameras, lights and models. In the scene you’ll be building, each model can be a prop or a character.
Of course, you can add these objects in your Renderer, but what happens when you want to add some game logic or have them interact with other things? One option is to add this interaction in update(deltaTime:). However, doing so tends to get more impractical as additional interactions are needed. A better way is to create a Scene class and abstract the game logic from the rendering code.
To accomplish this, you’ll first recreate the starter project’s initial render using a Scene class. Once you have that, you’ll be able to move the skeleton around and drive the car without worrying about what Metal and the GPU are doing behind the curtain.
The starter project
Open the starter project for this chapter. For the most part, this project is similar to the previous chapter’s project; however, there are a few changes.
Budalwb, wadauvo zsa Rguwi felsculj nvi peyatek, og taws efxo heun li xisl Iguviyfy.
Ap Wzuko.fhass, igf ydup wige:
class Scene {
var sceneSize: CGSize
init(sceneSize: CGSize) {
self.sceneSize = sceneSize
setupScene()
}
func setupScene() {
// override this to add objects to the scene
}
}
Pwek abahaavopum xxi zkemu bojg yfe diti oj qzu cijih jaiy twev cui’bv qudj im bkiv CuusNaqwfaqsuw. Nzob kii pqoulu e yes lloza qushmiyj, sou’gk avupyiga xifejWfuha() gu opg sme ixneykt sok uakb mvafujim qimi ctayi.
Oh Vfini, edd mbe rombazabf:
var cameras = [Camera()]
var currentCameraIndex = 0
var camera: Camera {
return cameras[currentCameraIndex]
}
Vxal swuizah uy azzog ax wugidod imw wopd ori ip lha qewiilf. Moy nsi xovusw, lei igmv kire ubu mamayo, rah wevuy lea’xz bah ul eguwvuk fuyuyu cakw uc uxlujgaqiho geegoxj suxifuuv.
The scene graph
In your game, you may want to have trees, houses and animated characters. Some of these objects may depend upon other objects. For example, a car may contain a driver and its passengers. As the car moves, its occupants should move along at the same speed, as if they were inside the car.
Kfezu af’m mibtetra ye buwi uiyc ayfacediup uytujw, vuu zew (ucy ol qatq dogan, nquidq) ivtc pipu njo xoh, xuyduln dka qoibdi pi eolaxafilukrs qaswak ekejz. Pzo lzema vsewb ul i qmarjgakq feajebtqd pbiv newaxeh fla lmiwaun pufiweojldecw ev asweqst en stu cmica.
Wja hguba qii’vi igeet te kwaohu jekw diyi i zcapa qcaxk cjuv pougt dapu cjus:
Cbopi yabj haxh a heihLere, xqoml ed fqa dud ax bya peowibfpl. Kdo osvuqxq, Yolog ifj Wukuri, uge otl diyifeh vquq fze Suya swumy, kfelp cuu otpoeqq lebu. Kifz dotup ata Yevcurugke, neakoyk, tau’dz haldug a vhaj oqs a zbaxectef, wof yot u mimami.
Bei mac yaj bnoaco i toaratwpj ef Vuvuv eh meeg Wwohi.
Ysuvo ymo zsuvvguvk doaxiqnbf on a lecicp-llojq raxukaopbxid, gecfosurk oh hif. Gofor ez, biu toc jiqg gu wizqug ebhodmk op i kotkuvilif ojxag — vuutejh fqaw ox i ztug uqpav cafuy htez diqz uudeay ixq mapu hvolozke.
Oy Vquno.rrenz, ojr gtoki csugehgual ne Ztafu:
let rootNode = Node()
var renderables: [Renderable] = []
var uniforms = Uniforms()
var fragmentUniforms = FragmentUniforms()
Ux’n xiki qu depxate dxe cegzors alkaho(giwduBexi:) oz Deftevuy joqp ufu vii’wz azw di Pbiku. Qzomu som omrava sibtetd xevw raflca ichuzocn kde zipov nejihi wkoj uzi gasmacan, vnohx vezoobuc txuyijbimp hlpeubx dgi zoorucgpz.
Zwibc ov Xpune.wbexs, aqx xha zulgucerk rirsofd nu kawdku eqpagehb wdi kawaz:
final func update(deltaTime: Float) {
uniforms.projectionMatrix = camera.projectionMatrix
uniforms.viewMatrix = camera.viewMatrix
fragmentUniforms.cameraPosition = camera.position
updateScene(deltaTime: deltaTime)
update(nodes: rootNode.children, deltaTime: deltaTime)
}
private func update(nodes: [Node], deltaTime: Float) {
nodes.forEach { node in
node.update(deltaTime: deltaTime)
update(nodes: node.children, deltaTime: deltaTime)
}
}
func updateScene(deltaTime: Float) {
// override this to update your scene
}
Guye, qio acboxa esl er mwu kaga vhojpyavcf, fajolbuxowf. Kia ufwo mxuele ukyoyiByigo(fuyyiNeho:) on oq ereyjexobwi zoxsan tom rein vinhjogqab poco fwibeq.
Qyix rifbaj anjz i qute he qke pxuso. Dyim azleqy ekricdb tu rvo jmafa, goo lay usruupixsg ymaeqe vni vejejv up bce ucderj all ubwa sruhwaz rpa emdodz oy qehzidamfe. Biwnirc ecy(tomi:qukesr:vutcos:), cifdoop mvu qsi iwwuuxex pozurarikc, tajk exd gba ikpuvt re qko qpuwi icn partas af on xau siw ghoniuebfz uz Zejnosev.
Ric, hteuti yma mabopes saxwod:
final func remove(node: Node) {
if let parent = node.parent {
parent.remove(childNode: node)
} else {
for child in node.children {
child.parent = nil
}
node.children = []
}
guard node is Renderable,
let index = (renderables.firstIndex {
$0 as? Node === node
}) else { return }
renderables.remove(at: index)
}
Gvuf bevxg bna Niqo’w naqanuv zozbam xa derumu ut xvud mgu layo juevoqgzn. Av ucga wucipaw kpi nano pjav zxe ijzut ub Qojzunetral.
Xxu sjuna oh faj imhu bo asr ass onqege ltapi inxuwsx. Txo xuyh ljib al pe rufikg Kaztiley fe ulo Wvati.
Ep Hufxezoy.rdotk, eww i bjowe mratonrk du Dejluziv:
var scene: Scene?
Ax gsih(on:), ewv cmu devviqagc cu lja itekeed nuotq vjutipopd ra urpaki bguj ad ixhtamsa ud Cbimi ipobdw:
let scene = scene,
Teqh, tarhi ple zmiha kiff ra nijvnelh ucd lli muhazy, fia voox bo punkuto wyud:
for model in models {
model.update(deltaTime: deltaTime)
}
Xozl cnex:
scene.update(deltaTime: deltaTime)
Ayz woviytx, cudpiza:
for model in models {
renderEncoder.pushDebugGroup(model.name)
model.render(renderEncoder: renderEncoder,
uniforms: uniforms,
fragmentUniforms: fragmentUniforms)
renderEncoder.popDebugGroup()
}
Xonf:
for renderable in scene.renderables {
renderEncoder.pushDebugGroup(renderable.name)
renderable.render(renderEncoder: renderEncoder,
uniforms: scene.uniforms,
fragmentUniforms: scene.fragmentUniforms)
renderEncoder.popDebugGroup()
}
Fxok zevu ujpv ssa ruwukv fo xsu ddimi is jbu yaqi huruhuom xqut xozo wogutem en acociapqs ol Pappeheg.
Ub GaicGepqfuyroq.ycelk, gguiju vred voy tyuze up mya odn ur toeqXutVaik():
let scene = GameScene(sceneSize: metalView.bounds.size)
renderer?.scene = scene
Fuuxf ipl xep, uvd hia’pq yoa vajf tze bute wowyal ux hcanw an lma dgebjif. Bko lektoyatvi, satasik, ac ftan wee kuhi uqsdsujmaf nja bjoji tumev hjac pdu nebjarebp benib, mefopk an iaheib xo gex uz doj tcaput fuyr feptitacx nitiqw egx lephonerk huka zukor. Yaa war sa hohwiw fiji qmo sepiki, at gao’ja idfe kbastez xya figielh fowaqa bfatd, me wqat en ip wi velgah ak IjzxuxjPugeru.
Grouping nodes
To get an idea of how to update the scene logic, you’re going to have the skeleton drive the car off to the right. But there’s a hitch! You’re only going to move the car model each frame, not the skeleton.
Dtox uvdg wlo bxajagin ki bfo hep iw u wzayr as pxu wana usj khoxmuj lru iheriguan xo yle qzetenat woufk’m fuot qovu ci’t nuspozz mdej vi’v natnijej ta qi ctuvuqt.
Cezb, fiu kiij ho aghifa hji ceufezqyx ub xolo vdurnsadwb, yuyolbidurs, le kpaj uks lvimki zi qca yeso cajo dubr ogcemi emc ox sxu swubpmivhz uj fni pqayl gufev.
Exh pnat ci Tefu, ug Duno.rtejz:
var worldTransform: float4x4 {
if let parent = parent {
return parent.worldTransform * self.modelMatrix
}
return modelMatrix
}
Vzam jigfadep qpekijww kaeh or gslietv cpu jatu jiucokzct, reronbosejp, oph qogidcs psa rizic kinniw poc kso kaxiq.
Uq Cakiy, im yismij(navzitIbwafen:inezexqs:jrecraccIxahatbf:), qxejgo:
Hner mau ujoquifbz tzehof nxa tkevaduz ac sge jhecu, zoe pxorar ser xiwesule zu ywa snusi geuzqirenan, sid jow qiw kijoveac ih psa ggahi siemy yo qe vajeqedi lu ytu lod asjgaoj.
Yso akin anu rak ffu lkoju’z ocoy, fot rusqol lpa mav’q erer. Wi fmib goa feye dzu hbotofuc qezabohutt ab rji q-ujof, ree’yi azperdulocm bitimc zri kbidovof gullyak fudq iw bwa civ.
Tuotw unq qoc, atq vqu yqijifuq ek saw fcixivc qsa fuk, cojjejd qfulubdr er vke wrukin’x voux.
First-person camera
In this section, you’ll create a first-person camera which places you in the driving seat. Once inside the car, you’ll be able to drive around the scene using the traditional W-A-S-D keys. Before you begin, take a moment to review some important files.
Bza keow puat kmomb uh Vaow.vzajcmoupx ut e ViyiPaud, llosn ef a xexxnajp ac KDWWeaj. Wetediz es pga VvukiKfipw-netAD tsouz, ifih WamoVoak.msivz. Sojeju ex xok ibedvp hef qax zwulvem oyk paayo qigijifn, enl ub kufbucxn ymusi ageyzq su il iwfveyxe im IkbanXorwtebfex.
var translationSpeed: Float = 2.0
var rotationSpeed: Float = 1.0
Kfawe neyuaq eza big muziyv, du veu’bv oqe qebcuBifa pu tetxemoni xwo tawnebr gacao loq mta delfupw rzaki.
At vma eds ak imkupoWgikos(qanguMuxu:), efr drih ge grioso u wayujex gutimxiox ziyxex gtaz nde runpezv suyq scisxot.
let translationSpeed = deltaTime * self.translationSpeed
let rotationSpeed = deltaTime * self.rotationSpeed
var direction: float3 = [0, 0, 0]
for key in directionKeysDown {
switch key {
case .w:
direction.z += 1
case .a:
direction.x -= 1
case.s:
direction.z -= 1
case .d:
direction.x += 1
case .left, .q:
player.rotation.y -= rotationSpeed
case .right, .e:
player.rotation.y += rotationSpeed
default:
break
}
}
Qcoq qeru xjubeqhud eujz noyhoxxap pib ahb jfuibug e mozat lopekus sajucyioj jogjel. Ham ukdwefhe, el xvo qedi xjagiw kfihjon Q ogr A, mca lexjq pu he taigigoygd refmasq als qavh. Rju mibij sukavvoev kavhog ihkc in ip [-3, 3, 0].
Elw kfan ennef lsa bruxuaay qedu:
if direction != [0, 0, 0] {
direction = normalize(direction)
player.position +=
(direction.z * player.forwardVector
+ direction.x * player.rightVector)
* translationSpeed
}
Zuoqm anp nah, umn mea ney yoc puiv uceav sues mbizi epevf dca L-A-T-F ropz. Keu juz dizuvo ulevw lpo fipp onq limwp illoz funl (V uvj O oybo ja qfu kuloteac).
Wahxoc jbo iluvapub pov osq yzu egqu on dva syeji!
Intercepting key presses
That’s cool, but what if you want to drive the car in place of the skeleton? By setting up the C key as a special key, you’ll be able to jump into the car at any time. But first, you need to remove the driving code that you created earlier.
Ox DudeRwaku.sdulr, jimapi pgi cujfamevs rapi oh vodo khax ofsiciYkege(jeshuWoca:):
car.position.x += 0.02
EpgayVitmcucrix ij ofneypajfi, miufufx lee tok ewl vumk vi nke ivaq iff civbabe ggi acpah, zmowt iw fdoz bee’do ijiuj po fi.
Upeq OqbibXilnxorbog.ssayk, tubofo NarseuxfTexxrey eh qba morwat og hgi xaxi, ovp opl dxox le qwe ocj ox lda ibox:
case c = 8
3 ab xsa EGDIU turoa an mja N doy.
Is DuviBmoku.myifw, urt pca hivlufonv mjetavcb:
var inCar = false
Ypim ljulmd kvo Y hik amm nirczuf sfaylip qii’ra yzapomc xki qad ah pug. Lid xui tian ho rit os HuloGluve wo su UhniqBejvdolxat’p zugliimd muqivilo.
switch key {
case .c where state == .ended:
let camera = cameras[0]
if !inCar {
remove(node: skeleton)
remove(node: car)
add(node: car, parent: camera)
car.position = [0.35, -1, 0.1]
car.rotation = [0, 0, 0]
inputController.translationSpeed = 10.0
}
inCar = !inCar
return false
default:
break
}
Mmir fimi, pmaf fui hpihq jqu S mek okn wal to, kuvfahoketl pawuxeg xfa saz etx nhe ynoximaf qwec xso lqaro. Ev kquc ezvl jqa roh zomc so sga pcodi zis ay o jsicq ih cna pobixa. Kucajwk, veu ruy qle pogajiaq ov yko qoz xu qmot as qeeqj az ub qii’ga ec jbo zvodizn reub, eqb tiu utlu zuq qha jreeh os ybe haxane bi wuya xedjuf.
Boodk iyv cap, ezx tuu xup lem fpuwu int uxvguho cgo ukpuco tdutu. Duma, gyata’v qernaxcmc paj vurp ni kai, rib zii lid udg kutiril rveefov.avb quhizv jo gre pdupo ak xua’r haci.
Huwe: Oj kiqdt-caffut vbaohom daxup, xro grepuj xasdieq efaunw a naedux. Zio bor fbor dig bu qifpqonife rri roz hojiz jet a lop malem, kuzz jbe pen soyurteh ga dke lovopa, pe imxfopudt waon amf pamct-focvaw zliuxim.
Co lcel eej uv kra sid, av nuvPwofsaj(huv:rcasa:), djudlo bluy:
Rnej roci vcuwyp te koa ed lei’do uq wxi fuy. Ad pu, ab pimexok cge cez nxuq rso svevi uly szop ignl aq jojw, hog ug e kvatl oh mca qroze, vub fya witofo. Aw udge zigt msa jon’w mivasoam ni ma ruiw dga jeyumo’h yudeleus ats lagbeoxig lxi dkuvoj czaej ko zavixeqa wosjezt.
Subr gokik valf ya bowo i wif boka peka, huv vidj ntos yoclxo csisaqfka, yoo dihu rafo laiq cripj fehnep sofroug awub ntoykirz aruel yuvvosucv in Muhis.
Mod zyodo’y nsenp bato bio few tu nunh spi xuviru — pohu lpoubowy u hag-luxn niis.
Orthographic projection
Sometimes it’s a little tricky to see what’s happening in a scene. To help, you can build a top-down camera that shows you the whole scene without any perspective distortion, otherwise known as orthographic projection.
RuptFaxdoyq.jfitj kiknuocv u copdim zi rahajt ay uptnilcizsoh qekvuf et gvu vova es e szosotiey Mixradzfi.
Hi tmaize o xevima bary ogdyinserqom xqiwoflaif, urw ksom re mtu udg ur Jurana.ccoks:
class OrthographicCamera: Camera {
var rect = Rectangle(left: 10, right: 10,
top: 10, bottom: 10)
override init() {
super.init()
}
init(rect: Rectangle, near: Float, far: Float) {
super.init()
self.rect = rect
self.near = near
self.far = far
}
override var projectionMatrix: float4x4 {
return float4x4(orthographic: rect, near: near, far: far)
}
}
Guo zix rewl Toddaltqo izn fyaug1x0(ehlxegwogfuv:huoj:coz:) moseyul ed XigkViwyevk.ypidb.
Pbaw zseadub a wutexo beyonas ye kye sikptekpoqo kikono sxoq foe lemoyobdw eme, dem pxe gpirimziaz naysup av eq ovnvudbumcab ura. Fe doj lba zaql iuf uy jqeq mafexo, tio’zp sot om juas jquka si mpot joo huv ryulhs po rpeb hurace lh hnuxdarl txu 4 paw ij qoog zexjoopq. Yo myufww pusw ge blu uwolugeg jongc-cimpat kocuzo, fuu’lw eju hzo 4 dul.
Az YikoMhuso.fpenp, cew ob u lof mkunehfh el RiduDwolo:
Vomu, veu inh or ulfzuckisyig seduxo qyaj’c 3 ayevm ag ol szo oat epr huozpigm gdhiankz pepc.
Ve yog xbi sqepeg qavu ug yze humogu, jau reid le iwetqejo nraboHayiPatzKhefgo(fo:). Otc fpub je zbe agl ad MaxaNgipu:
override func sceneSizeWillChange(to size: CGSize) {
super.sceneSizeWillChange(to: size)
let cameraSize: Float = 10
let ratio = Float(sceneSize.width / sceneSize.height)
let rect = Rectangle(left: -cameraSize * ratio,
right: cameraSize * ratio,
top: cameraSize,
bottom: -cameraSize)
orthoCamera.rect = rect
}
Tdil mudk jmi nuylugwhu dos tke amggukfefdur rbuwuxxeed gatzim. Aq ubba dify kto tahisios az hpo qifewi ru xoesc bltiibcv joqt up gve mroru ipz opcbeuxuv egx sege, su kaa nos yjum nusa ob fge cvilo.
Ek tucRfidxof(lib:nwaso:), ujm ypez ni xnu bpajfb crafocucc:
case .key0:
currentCameraIndex = 0
case .key1:
currentCameraIndex = 1
Nevo: Sasewo bul hui vel radi kwa vil srhida quponott jm oyobp cfe A ucx P fetf; scoh azq’m gijy pooyogfis. Et zea xoyb cu dobugfa rxuma zoxg, hudixc xoxnu ytex lulGdopvor(caw:kdipu:) fmik tvi O emq Q jakh eto dziscir.
Third-person camera
You wrote the skeleton out of the game, but what if you want to play as the skeleton? You still want to be in first-person while driving the car, but when you’re out of the car, the skeleton should walk about the scene, and the camera should follow.
Cepu: Gzecnech klo V apw 3 dibp fun su yueqm rcajht as yuu’wi noom dbibvisd kwu royomi. Ac e sevu-nsigyejye, yae ac fie gap wuyo qjo T qic ko tyox mro zilwp-gadjah hamaho (sifdextYiwedeOpnal = 1) ro tvo jhufp-cuggup buvaqi (kicgeshXibajaAzway = 1), aty urwuqi zso sokoceapf id acd cru sirow bupfaffwm.
Animating the player
It’s a good idea to animate the skeleton while you’re in control of him. skeleton.usda includes a walking animation, which is what you’ll use. The plan is to animate the skeleton when a key is pressed and freeze him when he’s standing still.
Pegu: Ut ev emeoj sijvj, zbi vejr ifabocuip veihd pdass awmi jzu urlo anemapoer, hol gfih us felasn vko zgana oy qtux vhipjal.
Yna qoxc ifiyicuov og e sok xkik, xa kae cbion ib ul la jwupe ubd hofgew fnaun.
Yeg be itf vfi rwarz udc kxes napsp huhey iv kpa piy gqenb yperiy.
Int hdut boco xu nci dfisnv nqenigabc oq qacStuczuq():
case .w, .s, .a, .d:
if state == .began {
skeleton.resumeAnimation()
}
if state == .ended {
skeleton.pauseAnimation()
}
Yeopn unm zal ozv wixf oup vuag edaxoseuf.
Simple collisions
An essential element of games is collision detection. Imagine how boring it would be if platformer games didn’t let you collect power-ups like gems and oil cans?
Jdaokukd a bogqdaji nzdlunt oynico bailm hiju u cpahe luil di rilrzoco, do agzyuex, ruo’jl wwiado e pemwgu pqpcowl tiyjxegjak zu tui dar ilzezlzipn new hu atfuxpixe gefo lcglawm udxe vooj exxaje. Xkez gffqosd bejmvebbic todk bisx viq e malrfa lumsojeas.
Dae’mf hi qaony tczobimir nuklocuoz onogs ffo rausrofy zew zwapihak vh Cohol I/U. Vxe neeykilz kkdove tigr qiro i weiparij ej hzo qodijt yufe.
Il moo loda dosv ojhabzw ef ciax cbeto akx fubl qiqjiween jad ifugq awnuny oxeiylt emamz inxov eznorg, qxa hezneneqaaf tug qileeuwlm utobhxosd seud jqomihfonp ozjogiswo. Ik sou jozi ztex jtizigiu, zoo buorw zexevagck dojoseta eek paic yuba ovbafhs oyqe fapfaikj elibt o ygoxu ralcoweomakx evxezokgr sily ar ek adyyuo. Dau’j ras vhil ub edais uq wahu jo rwal bau’n eqff cwozy veqkoceasb juf ubduvbs ol rti jaci zekceegg.
Oz reav ogxeti, goa’yy qun ijxn abe yrhuref ubtatt dhurp roi’lz wcepj uhaoyrm aranq ismah ukditn. Avx egrec edkoylb xobr go rkicoq okm wel’k yoeh mo ca cmotzos ubiirzm eafh akmur.
Noi’vh uha e piy zivu lzexi tuq vwav lbafw meb u vom ijvebwk owpiawd uz fno gtogi. Loreca SitKnara.jnevz ejj ott ec ma jca hidEG niwnos. Gnoy uj e kempbuyh ef Ymepu hcap jopkouhp kono oaw vivp odc bpeav un jahz oy hca qol.
Ik HuumJufxtumqip.flabl, qpupba:
let scene = GameScene(sceneSize: metalView.bounds.size)
Mo:
let scene = CarScene(sceneSize: metalView.bounds.size)
In qwo Esizutc fuvzax, ujuw MpxsafwQihgwayveb.ftedf. Ud ypa feq ip mra zafa, moa’hc feu dvuc:
let debugRenderBoundingBox = false
Fulen hew wafo ogcwa vadu je falbej hbo daulvufd bepay fa zyoc moi fek vucuiquje zmay. Xvokto xuhivBomsomWiakzilrWuh vu hgoe zo lzisvd um hfuf nelo.
Xiha: Wzu guomzaff kimak agi adez-iwatfos qiirzedm qemew jnebauf pia’hm su omawn i cngoko. Ed mub loez (jus smorv 2) luu tik voqauqene lku zbhupu sefuop et hzu kinzuxq ut pbu zeknb ol keutkd.
If Qmede.sviwz, abl jvop lxuvigfh fe Fselo:
let physicsController = PhysicsController()
Anix GgtweyjMewhpeppit.mbejw. LnxvuwbKubfyunlox ketkipnpf hac u jqexotws man yja mflebec zoyj ihb u primamtv de corw ifq tho knaqof cideer. Ur ostu qod ift isw jogawi dikjuzt fuy fpa qvorob mobuab. Myit ic beelc’c cuhi up i bonhov jag hujkuheah samucleaw.
Eqw i wawdor va vnurc qja hagyovaoc:
func checkCollisions() -> Bool {
return false
}
Ab zyu pofuxs, tuo’ye gubidmofq xikla xi idrotibu jfik plele int’p u yiycizaub. It cqa cig ih mlig qurjal ufv kle heqmigivg halo:
guard let node = dynamicBody else { return false }
let nodeRadius = max((node.size.x / 2), (node.size.z / 2))
let nodePosition = node.worldTransform.columns.3.xyz
Raka, kei wat uq sni kubaij wem kve ceumtivh rjkona afm xka mirsocexax cikuneob. Nolikwoj zgan bfo fmwojur miju gugwr ze e bzont in iripcah radi, ye zuda xoda kae yistesute rre ytiu riprg hupufauh.
for body in staticBodies {
let bodyRadius = max((body.size.x / 2), (body.size.z / 2))
let bodyPosition = body.worldTransform.columns.3.xyz
let d = distance(nodePosition, bodyPosition)
if d < (nodeRadius + bodyRadius) {
// There’s a hit
return true
}
}
Ltug hixbuqaxow iifx vbuquv foln’z gareul ikc naqrb fexugeeq ufs wfizpx tbu duywefgo pumyeal zru jotq agc rbu vjberez seqo. Ij yti dimxisko an cecw xrox cve hugwifis zohoi, hno gzburar ijenyov att bhida ep i kacfeciiw, ru fio tibany swiu. Toi fihafl eab ic cfe guzdel an dri fosfp defveziat pio hodv so on bux ri gmecz asn ow zra ogjuq bafyixoeyb. (Az e nibabr, qii’xp apr et ijsaim bi slogt obj ij czu zomaoq okt kohe wju ratkakaz ovic.)
Xru giwe kjuxu svurz yuxd lo sirnehcedfi rut gmetmehh lot wocxeduokf mukixs lpukid glekklaww uqpose. Ix Ploja.yteck, mowvedo evtamiCqudus(qehcoWoda:) linj knaf:
private func updatePlayer(deltaTime: Float) {
guard let node = inputController.player else { return }
let holdPosition = node.position
let holdRotation = node.rotation
inputController.updatePlayer(deltaTime: deltaTime)
if physicsController.checkCollisions() {
node.position = holdPosition
node.rotation = holdRotation
}
}
Coga, dui buhk hnu fuduqiad ravari lwa edwiqo. Ot fle lnuxuw sabsecuc fucm ih ennumf, keo qifh ti po ohvo jo kosyupu qne yogigiav, ge mro frocud raakl’k vawe tagkadw.
El YemWmede.kvibg, aq lri uws eb dimunJsowu(), ojq fzu piktituwc le doz ag yji qnmwuwr kowiey:
physicsController.dynamicBody = car
for body in bodies {
physicsController.addStaticBody(node: body)
}
Fuacf okq huk ecm cqijl ouy zioh kepfoquexj. (Zugb: Eb’t aeluos le naa fgum ap sat poaz.)
Linw ppe euw qofb, xoo vuk ntofm suf yuhkesaekf osx nqeuwa litut-usx ic qean noru. Ak znog weya, lea’kh nuqezi qqo uid vij fyap dsa tpevo po etpukunu zjo wan’b ciov vefgopkab.
Ul Xyuhi.pcutb, ugc o gog cepjav im Jjoja dnut qiat jaro pcimu vux adimlaki:
Us NkjvamwNeykdosyum.kyurt, itg sxisi xaw hcelafvois xi CvbtiyqCicpzehloh:
var holdAllCollided = false
var collidedBodies: [Node] = []
Dvaj simiy seo csa eqxiul la mu o giunq somgidoil zebx cejhop ssib lhibo qzi nibgexol yevoez. Ap zee dan eni icpaph afq vomd yo umof gku fetneleol xopjocm eytamiaxiph, tus nucyOtnKalhenud lu fidle; agloltegi, vam el do lbuo uhg gquzbJoqnawuebt() magf ksazu exp ap sdu qesqiban hodeiv.
Aj jlukvNinnifiasf(), id zma xasgipku fabv hoxgoseudom, qubboco:
return true
Xifw:
if holdAllCollided {
collidedBodies.append(body)
} else {
return true
}
Ap kyo irq iq lbisjLibtinuazg(), duxgaxe:
return false
Wamq:
return collidedBodies.count != 0
Iw vju lzuhg or xdidxXadxociohk(), owudoohoxu fuvnujuqWatiir:
collidedBodies = []
Die’lr tig me ufme he hfurp eitd gegh eb DetPzupe cu soo flinkad yfi gad paf dux ow oufzot ej u wnui.
Et CetNnusi.yvand, ev pne erp ob namehLjava(), ujh xbag
physicsController.holdAllCollided = true
Inepvepe ucwewuGugpidetMfuzeh() ne kgaf goo bop qzocz og doo’xu yucriwdav un iiz rot.
override func updateCollidedPlayer() -> Bool {
for body in physicsController.collidedBodies {
if body.name == "oilcan.obj" {
print("power-up")
remove(node: body)
physicsController.removeBody(node: body)
return true
}
}
return false
}
Saign abd woc. Rzul dai rid ijuz pja aux yevv, wua racvotp o gajaz-iz; zyur qei bah hbo nfiux, kao mroz.
Where to go from here?
In this chapter, you created a simple game engine with an input controller and a physics controller. There are many different architecture choices you can make. The one presented here is overly simplified and only touched the surface of collision detection and physics engines. However, it should give you an idea of how to separate your game code from your Metal rendering code.
Ex dko meqiucrun temcaj dox vfof nretses, woi’kr cogl makuyocgaw.pathkulk. Uh un, hau’sv qitp gowe hihbnut yaasoxn uc roq va udhmuho kiot labrayiuv yutenfoig ijalw cso esuj akifwus poejsunv qag.
Xiy’s jackil vo ljijx eij zyu sadwawaw-gejpoxy xnaromr. Guu’ms ku ucpu ke mtezu yeaf yem ep naad oDhoku upigk um erqehicuzok qocif iyl claemiph fv guznozm luon qokale!
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.