;(function() { window.createMeasureObserver = (measureName) => { var markPrefix = `_uol-measure-${measureName}-${new Date().getTime()}`; performance.mark(`${markPrefix}-start`); return { end: function() { performance.mark(`${markPrefix}-end`); performance.measure(`uol-measure-${measureName}`, `${markPrefix}-start`, `${markPrefix}-end`); performance.clearMarks(`${markPrefix}-start`); performance.clearMarks(`${markPrefix}-end`); } } }; /** * Gerenciador de eventos */ window.gevent = { stack: [], RUN_ONCE: true, on: function(name, callback, once) { this.stack.push([name, callback, !!once]); }, emit: function(name, args) { for (var i = this.stack.length, item; i--;) { item = this.stack[i]; if (item[0] === name) { item[1](args); if (item[2]) { this.stack.splice(i, 1); } } } } }; var runningSearch = false; var hadAnEvent = true; var elementsToWatch = window.elementsToWatch = new Map(); var innerHeight = window.innerHeight; // timestamp da última rodada do requestAnimationFrame // É usado para limitar a procura por elementos visíveis. var lastAnimationTS = 0; // verifica se elemento está no viewport do usuário var isElementInViewport = function(el) { var rect = el.getBoundingClientRect(); var clientHeight = window.innerHeight || document.documentElement.clientHeight; // renderizando antes, evitando troca de conteúdo visível no chartbeat-related-content if(el.className.includes('related-content-front')) return true; // garante que usa ao mínimo 280px de margem para fazer o lazyload var margin = clientHeight + Math.max(280, clientHeight * 0.2); // se a base do componente está acima da altura da tela do usuário, está oculto if(rect.bottom < 0 && rect.bottom > margin * -1) { return false; } // se o topo do elemento está abaixo da altura da tela do usuário, está oculto if(rect.top > margin) { return false; } // se a posição do topo é negativa, verifica se a altura dele ainda // compensa o que já foi scrollado if(rect.top < 0 && rect.height + rect.top < 0) { return false; } return true; }; var asynxNextFreeTime = () => { return new Promise((resolve) => { if(window.requestIdleCallback) { window.requestIdleCallback(resolve, { timeout: 5000, }); } else { window.requestAnimationFrame(resolve); } }); }; var asyncValidateIfElIsInViewPort = function(promise, el) { return promise.then(() => { if(el) { if(isElementInViewport(el) == true) { const cb = elementsToWatch.get(el); // remove da lista para não ser disparado novamente elementsToWatch.delete(el); cb(); } } }).then(asynxNextFreeTime); }; // inicia o fluxo de procura de elementos procurados var look = function() { if(window.requestIdleCallback) { window.requestIdleCallback(findByVisibleElements, { timeout: 5000, }); } else { window.requestAnimationFrame(findByVisibleElements); } }; var findByVisibleElements = function(ts) { var elapsedSinceLast = ts - lastAnimationTS; // se não teve nenhum evento que possa alterar a página if(hadAnEvent == false) { return look(); } if(elementsToWatch.size == 0) { return look(); } if(runningSearch == true) { return look(); } // procura por elementos visíveis apenas 5x/seg if(elapsedSinceLast < 1000/5) { return look(); } // atualiza o último ts lastAnimationTS = ts; // reseta status de scroll para não entrar novamente aqui hadAnEvent = false; // indica que está rodando a procura por elementos no viewport runningSearch = true; const done = Array.from(elementsToWatch.keys()).reduce(asyncValidateIfElIsInViewPort, Promise.resolve()); // obtém todos os elementos que podem ter view contabilizados //elementsToWatch.forEach(function(cb, el) { // if(isElementInViewport(el) == true) { // // remove da lista para não ser disparado novamente // elementsToWatch.delete(el); // cb(el); // } //}); done.then(function() { runningSearch = false; }); // reinicia o fluxo de procura look(); }; /** * Quando o elemento `el` entrar no viewport (-20%), cb será disparado. */ window.lazyload = function(el, cb) { if(el.nodeType != Node.ELEMENT_NODE) { throw new Error("element parameter should be a Element Node"); } if(typeof cb !== 'function') { throw new Error("callback parameter should be a Function"); } elementsToWatch.set(el, cb); } var setEvent = function() { hadAnEvent = true; }; window.addEventListener('scroll', setEvent, { capture: true, ive: true }); window.addEventListener('click', setEvent, { ive: true }); window.addEventListener('resize', setEvent, { ive: true }); window.addEventListener('load', setEvent, { once: true, ive: true }); window.addEventListener('DOMContentLoaded', setEvent, { once: true, ive: true }); window.gevent.on('allJSLoadedAndCreated', setEvent, window.gevent.RUN_ONCE); // inicia a validação look(); })();
  • AssineUOL

Topo

Virtual Cities: o livro que cataloga lugares dos games como cidades reais

Gael Mota

Colaboração para o START

17/04/2021 11h00

Virtual Cities é um livro que reúne 45 cidades que visitamos em diferentes jogos e as explora como atlas de centros urbanos reais, contendo mapas com ilustrações originais, design, história e particularidades desses lugares.

Seja em cidades futuristas como a Citadel, de Mass Effect, pós-apocalípticas como a Racoon City, de Resident Evil, ou quase reais como a Nova Iorque de Spider-Man, os videogames nos permitem sentir um gostinho de como é andar por aquelas paisagens urbanas reais ou fantásticas. E até escolher algumas para criar memórias e histórias.

Muito do que faz videogames serem tão fascinantes é o poder que eles têm de nos imergir em seus mundos. Mundos esses que, muitas vezes, são representados por cidades, seja algo mais próximo do real, como a Los Santos de GTA V, ou futurista como a Night City de Cyberpunk 2077.

Mas para isso se tornar possível é preciso muito estudo e trabalho, pesquisando os elementos mais importantes das cidades do mundo real e transportando para um ambiente virtual, já que cidades (reais) são muito mais que ruas, prédios, praças e avenidas.

Livro Virtual Cities cidades games - Reprodução/START - Reprodução/START
Páginas do livro Virtual Cities
Imagem: Reprodução/START

A cidade é feita de pessoas em interação, como eu e você, uma dinâmica complexa que nos mostra o grande desafio que é "dar vida" às cidades virtuais.

Quanto mais dinâmica, instigante e complexa é a paisagem urbana ali representada, maior será nossa curiosidade em entendê-la e explorá-la. E, quando nos damos conta, já estamos criando momentos épicos, tirando fotos e analisando os hábitos daquelas habitantes virtuais que nem conhecemos, mas já consideramos pacas (nossos queridos NPCs).

Em suas 224 páginas, Virtual Cities explora essas características de algumas cidades dos jogos. Entre elas, são destaques no livro:

  • Anor Londo (Dark Souls)
  • Novigrad (The Witcher 3)
  • Metro City (Final Fight)
  • Raccon City (Resident Evil 2)
  • Silent Hill (Silent Hill)
  • Clock Town (Zelda: Majora's Mask)
  • Kamurocho (Série Yakuza)
  • New Donk City (Super Mario Odyssey)
  • Arkham City (Batman: Arkham City)
  • City 17 (Half-Life 2)
  • New Vegas (Fallout: New Vegas)
  • Citadel (Série Mass Effect)

Livro Virtual Cities

Infelizmente Virtual Cities está disponível apenas em inglês, mas já está à venda online. O START conversou com o autor do livro, Konstantinos Dimopoulos para saber mais sobre a obra. Assista ao vídeo no topo da matéria.

SIGA O START NAS REDES SOCIAIS

Twitter: https://twitter.com/start_uol
Instagram: https://www.instagram.com/start_uol/
Facebook: https://www.facebook.com/startuol/
TikTok: https://www.tiktok.com/@start_uol/
Twitch: https://www.twitch.tv/start_uol