Alter Ego: s duší v patách

18. 5. 2012
Doba čtení: 3 minuty

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
Někdy i jednoduchý nápad dokáže udělat z obyčejné plošinovky zajímavou herní hříčku a dodat suchému obsahu bez příběhu šmrnc. V Alter Egu si zahrajete rovnou za dvě postavy a jejich společnou kooperací se budete snažit dosáhnout konečného cíle. Vše je navíc stylizováno do příjemné retro 16bitové grafiky.

Mít rozdvojenou osobnost nemusí být vždy vážným zdravotním problémem. Takový parťák do nepohody se může někdy hodit a dokonce vám i píchnout v situacích, které byste jako samotný jedinec nezvládli. Pokud vašeho avatara doprovází jeho vlastní Alter Ego, může se z lehce tradiční skákačky, i když bez skákání, stát velmi zajímavá logická hra, se svěží a neotřelou hratelností, a jak má studio Retrosouls zvy­kem, i s retro stylizací.

Příběh se klasicky, jak to bývá u menších hříček, někam poděl(al). No, jednoduše nečekejte žádné epické fantasy, ale i ve hře beze slov a jediného psaného dialogu můžete zažít kouzelné dobrodružství. Octnete se v těle malé bytosti, těžko říct, jakou roli ve světě hraje, ale dle vzezření bych typoval nějakého bůžka. Ten cestuje se svou duší. To by nebylo nijak zvláštní, kdyby se nenacházel mimo své fyzické tělo a to se plížilo za vámi jako stín.

Obrázky ke hře Alter Ego

Ať žije vaše nesmrtelná duše

Během svého hraní tak vlastně ovládáte dvě postavy. Bůžka a jeho duši. Ty od sebe navzájem kopírují své pohyby, ale sem tam se stane, že jednají přesně opačně. Pokud se tedy vydáte doleva, duše se vydá doprava či nahoru nebo dolů. Naštěstí je ale váš nefyzický společník nesmrtelný a nepřátelé mu nemohou ublížit. Prostě skrze něj projdou, jako by byl jen obláček páry.

Cílem hry je sesbírat všechny „ťuplíky“ na mapě. Jakmile se vám to povede, postupujete do další úrovně. Samozřejmě se vám cestu budou snažit zkřížit nepřátelé. Stačí, aby se jich vaše fyzická stránka jen malinko dotkla a hra ihned končí a musíte celou úroveň absolvovat znova. Další věcí, co vám stojí v cestě, jsou propasti. Vaše postava neumí vůbec skákat.

Obrázky ke hře Alter Ego

Co je vlastně na Alter Egu jiné?

A tím se dostáváme k tomu, co Alter Ego tak odlišuje od ostatních plošinovek. Vaše fyzické tělo můžete přesunout na místo duše a naopak. Prostě se teleportujete z místa na místo a tak překonáte propast, dostanete se za zeď nebo se vyhnete stvůrám, které vám chtějí ublížit. Počet pokusů je ale omezený. V některých úrovních dokonce bude tuhle schopnost nemožné použít. Což vám může pořádně zavařit.

Hra by ale jistě brzy omrzela, kdyby se pořád jednalo o to samé. Předmětů, které máte sbírat, abyste postoupili do další úrovně, je několik druhů. Některé mají barvu modrou a sebrat je může jenom duše vašeho reka. Pak tu máme ještě druh červených, a ty získáte jedině, když se nacházejí v dráze transportu těla na místo duše. Tahle drobná úprava může udělat z na první pohled jednoduchého levelu celkem složitý hlavolam, v kterém stačí k neúspěchu i jediná chybička a nepromyšlené používání vaší speciální schopnosti.

Obrázky ke hře Alter Ego

Atmosféře pomáhá retro stylizace a pohádkový svět

Ruský tým Retrosouls se snaží o retro stylizaci nebo atmosféru svých her. Některé jejich výtvory jsou dokonce remaky zapomenutých legend. V Alter Egu proto sází na 16bitovou grafiku. To ale vůbec nevadí, protože svět je i tak kouzelný. Při hraní jsem si vzpomněl na animované filmy od Miyazakiho. Herní svět se mění každých deset úrovní a všechny jsou stylizované do temné ale magické atmosféry.

Herní soundtrack jde ruku v ruce s retro grafickou stylizací. Opět se každých deset úrovní mění styl. Soundtrack je docela adrenalinový a přišlo mi, že se úplně nehodí do pohádkové atmosféry hry. Když jsem zvuky vypnul, měl jsem z hraní daleko větší požitek. Hudba na mě působila jako pěst na oko. Ostatní zvuky naleznete jen ve formě občasného pípnutí.

Obrázky ke hře Alter Ego

Chyby a závěrečné hodnocení

Během hraní jsem nenarazil na žádný závažný bug nebo chybu. Objevil se jen běžný neduh logických her, a to obtížnost, která mohla být rozhodně větší. Nebylo moc úrovní, kde bych si několik minut musel lámat hlavu nad správným řešením. Kolikrát jsem prostě v levelu risknul jít bez rozmyslu a často to vyšlo. Kdyby se má šedá kůra mozková víc namáhala, moc bych se nezlobil.

linux_sprava_tip

Alter Ego je příjemná logická hra a i když moc hráče obtížností úkolů nepotrápí, užijete si ji až dokonce. Má svěží a jednoduchý nápad, který prostě funguje. Škoda jen, že celkem silnou atmosféru kazí nepříliš vhodný soundtrack. Nebýt tohoto neduhu, bylo by hodnocení ještě o ždibíček větší.

Výhody

  • Množství herních levelů

  • Herní prvky

  • Magická atmosféra

Nevýhody

  • Hudba

  • Nízká obtížnost

  • Bez příběhu

'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »