PsichiX

Members
  • Content count

    14
  • Joined

  • Last visited

About PsichiX

  • Rank
    Member

Contact Methods

  • Website URL
    psichix.io
  • Twitter
    psichix

Profile Information

  • Gender
    Male
  • Location
    Poland

Recent Profile Visitors

71 profile views
  1. aaaaand sallary is what exactly? because if you're looking for 5 years of game development experience and bachelor's degree (how does this even apply today? O.o) so you should provide any sallary information to not waste time of anyone you're looking for
  2. Tutorial #3 - Advanced yet easy to use input handling with InputHandler component Playground project: http://oxygen.experiments.psichix.io/p/r15hPq9EG Live experiment: http://oxygen.experiments.psichix.io/r15hPq9EG Based on "simple entity movement" tutorial, first we modify game.json to add InputHandler engine component to root entity: { "name": "root", "components": { "Camera2D": { "zoomOut": 2048, "zoomMode": "keep-aspect" }, "InputHandler": {} } } So now we can use it to respond to player input, which layout is defined in config.json asset: { "assets": [ "shader://sprite-transparent.json", "shader://text-outline-transparent.json", "image://rabbit.png", "image://snake.png", "font://verdana.fnt", "scene://game.json", "scene://snake.json" ], "input": { "axes": { "x": { "keys": [ "A", -1, "D", 1 ], "gamepadAxis": "primary-x" }, "y": { "keys": [ "W", -1, "S", 1 ], "gamepadAxis": "primary-y" } } } } There is input property in config asset, which defines input mapping to input axes used by game. Those axes are "x" and "y" for left-right and up-down movement and each axis has mapping to keys for negative and positive values, and to gamepad axes of primary (left) stick. And the last part to change is RabbitController.js, and here we remove all WSAD related key status properties and replace them with reference holding InputHandler instance from root entity. Then in onUpdate instead of checking all key statuses, we just ask input handler for x/y axes values which comes from mapped keys to axes by names. So everytime you press and release WSAD key, it converts to x/y axes values, so we can easly get their values as numbers. Yeah, that's a lot of less code! InputHandler, yay! import { Script, System } from 'oxygen-core'; export default class RabbitController extends Script { static get propsTypes() { return { speed: 'number' // units per second }; } static factory() { return new RabbitController(); } constructor() { super(); this.speed = 100; this._input = null; } dispose() { super.dispose(); this._input = null; } onAttach() { // get config data from asset. const { AssetSystem } = System.systems; const config = AssetSystem.get('json://config.json').data; // find root entity and it's input handler, then apply input layout to it. const root = this.entity.findEntity('/'); this._input = root.getComponent('InputHandler'); this._input.setup(config.input); } onUpdate(deltaTime) { deltaTime *= 0.001; const { entity, speed, _input } = this; const { position } = entity; // calculate delta movement based on input handler axes defined in config. const dx = _input.getAxis('x') * speed; const dy = _input.getAxis('y') * speed; // apply new position. entity.setPosition( position[0] + dx * deltaTime, position[1] + dy * deltaTime ); } } ---------- Next tutorial: Simple UI and multiple scenes
  3. Hi! Today we will learn how to make simple entity movement for player and enemies, also we will use PrefabInstance engine component to instantiate snake prefab on scene. Playground project: http://oxygen.experiments.psichix.io/p/rkP6JOK4M Live experiment: http://oxygen.experiments.psichix.io/rkP6JOK4M NOTE: Example uses Kenney's Animals asset pack. Open game.json file from Files list (application menu) and you'll see full tree of nested entities that builds our game: { "name": "root", "components": { "Camera2D": { "zoomOut": 2048, "zoomMode": "keep-aspect" } }, "children": [ { "name": "rabbit", "components": { "Sprite": { "shader": "sprite-transparent.json", "width": 284, "height": 370, "xOrigin": 0.5, "yOrigin": 0.5, "overrideBaseTexture": "rabbit.png" }, "RabbitController": { "listenTo": [ "key-down", "key-up" ], "speed": 500 } } }, { "name": "snake", "components": { "PrefabInstance": { "asset": "snake.json", "count": 5, "components": { "Randomizer": { "position": [2048, 2048] }, "SnakeController": { "bounds": [1024, 1024] } } } } }, { "name": "ui-controls", "transform": { "scale": 3, "position": [0, -1024] }, "components": { "TextRenderer": { "shader": "text-outline-transparent.json", "font": "verdana.fnt", "halign": "center", "valign": "top", "color": [1, 1, 1, 1], "colorOutline": [0, 0, 0, 1], "text": "Use WSAD keys to move Rabbit around" } } } ] } In last tutorial we had very simple single level tree (root entity with all components attached to it) - now we have nested entities structure: + root |- rabbit (player) |- snake (snake prefab instantiator that will create 5 snake instances on scene) |- ui-controls (text that tells how to move Rabbit) Root now uses only Camera2D to render game scene (root entity and it's children); Rabbit is our player and it uses Sprite to render rabbit image and RabbitController that will move rabbit with WSAD keys; Snake uses PrefabInstantiator to instantiate snake prefab as 5 snake instances; Ui Controls uses TextRenderer to display handful information on screen. Let's take a look at RabbitController component: import { Script } from 'oxygen-core'; export default class RabbitController extends Script { static get propsTypes() { return { speed: 'number' // units per second }; } static factory() { return new RabbitController(); } constructor() { super(); this.speed = 100; // status of WSAD keys. this._left = false; this._right = false; this._up = false; this._down = false; } onKeyDown(code) { if (code === 87) { this._up = true; } if (code === 83) { this._down = true; } if (code === 65) { this._left = true; } if (code === 68) { this._right = true; } } onKeyUp(code) { if (code === 87) { this._up = false; } if (code === 83) { this._down = false; } if (code === 65) { this._left = false; } if (code === 68) { this._right = false; } } onUpdate(deltaTime) { // convert delta time to seconds. deltaTime *= 0.001; // get entity, speed and WSAD keys status. const { entity, speed, _up, _down, _left, _right } = this; // get entity position. const { position } = entity; // calculate delta movement based on WSAD keys status. let dx = 0; let dy = 0; if (_up) { dy -= speed; } if (_down) { dy += speed; } if (_left) { dx -= speed; } if (_right) { dx += speed; } // apply new position. entity.setPosition( position[0] + dx * deltaTime, position[1] + dy * deltaTime ); } } In constructor we initialize non-pressed WSAD keys status, then onKeyDown changes WSAD key status if W, S, A or D key is pressed, and onKeyUp resets them. But be careful: those events are triggered ONLY if RabbitController component has set listenTo property to array of accepted events (this is property derived from Script component that our RabbitController extends and it have to contain a list of input events that our script want to listen to - making explicit listenTo setup has optimization reason, because Script can listen to many events, and most of the time we will want to receive only few (or none) events that our script needs). Then onUpdate process rabbit logic, so it checks which keys are hold and then move in corresponding directions. NOTE: handling input with direct events methods is not recommended way to do it - you will be glad using InputHandler engine component, which will be covered in next tutorial, so keep in mind that direct event listeners are the low-level way to handle input, but in most if not all cases you'll use InputHandler component. Next is SnakeController component used as snake AI that goes in random directions: import { Script } from 'oxygen-core'; export default class SnakeController extends Script { static get propsTypes() { return { speed: 'number', // units per second bounds: 'array(number)', // [x, y] changeDirectionDelay: 'number' // delay in seconds. }; } static factory() { return new SnakeController(); } constructor() { super(); this.speed = 100; this.bounds = [0, 0]; this.changeDirectionDelay = 2; this._direction = 0; this._changeTimer = 0; } selectDirection() { // randomly pick one of 4 movement directions. this._direction = (Math.random() * 4) | 0; } onUpdate(deltaTime) { deltaTime *= 0.001; // change direction when timer goes to 0. if (this._changeTimer <= 0) { this.selectDirection(); this._changeTimer = this.changeDirectionDelay; } else { this._changeTimer -= deltaTime; } const { entity, speed, bounds, _direction } = this; const { position } = entity; let dx = 0; let dy = 0; switch (_direction) { case 0: dx = -speed; break; case 1: dx = speed; break; case 2: dy = -speed; break; case 3: dy = speed; break; } let tx = position[0] + dx * deltaTime; let ty = position[1] + dy * deltaTime; // trim target position to boundaries. tx = Math.max(-bounds[0], Math.min(bounds[0], tx)); ty = Math.max(-bounds[1], Math.min(bounds[1], ty)); entity.setPosition(tx, ty); } } Snake AI is very stupid because it just picks some random direction at some time point so it's movement algorithm is very easy: if there is time to change direction, pick new randomly; depending on which direction is currently used, change delta position, then clamp target position to movement area boundaries and apply that target position to entity. Now it's time for Randomizer - because we instantiate 5 snakes on scene, all of them will be placed at the same position on scene, so to place them at different positions, we use this component to randomize entity position when created on scene: import { Script } from 'oxygen-core'; // This component makes entity initial position random. export default class Randomizer extends Script { static get propsTypes() { return { position: 'array(number)' // [x, y] }; } static factory() { return new Randomizer(); } constructor() { super(); this.position = [0, 0]; } dispose() { super.dispose(); this.position = null; } onAttach() { super.onAttach(); // get entity and randomized position range. const { entity, position } = this; // check if position range is 2 item array. if (!!position && position.length >= 2) { // randomize initial position in range. entity.setPosition( (Math.random() - 0.5) * position[0], (Math.random() - 0.5) * position[1] ); } } } So now when we covered all components, it's time to look at snake.json prefab: { "name": "snake", "components": { "Sprite": { "shader": "sprite-transparent.json", "width": 284, "height": 321, "xOrigin": 0.5, "yOrigin": 0.5, "overrideBaseTexture": "snake.png" }, "Randomizer": {}, "SnakeController": { "speed": 500, "changeDirectionDelay": 1 } } } As you may see, it looks exactly like any scene - that's correct! Prefabs and scenes are the same, and reason for that is that at some point of development you may want to make one scene with sub-scenes within (maybe loading time optimizations, or just want to put part of scene tree in different files to keep main scene small, readable and make editing easy). We also had to add new assets to game config, so game will load rabbit and snake images, also snake prefab. ---------- This is everything for this tutorial, next one will be: Advanced yet easy to use input handling with InputHandler component
  4. GLSL Texture Buffer Manipulation

    and exactly for that you use RTT with doublebuffering - it will give you super fast particle simulation. idea is simple: on start fill rtt A with initial fluid data and on render set target to rtt B and bind rtt A texture, perform simulation, swap target with source, repeat.
  5. List of tutorials: Hello, Playground! - Introduction to Oxygen Playground with Live Experiments services used to learn easly about Oxygen engine Simple entity movement - Introduction to input interactions and prefab instantiation, also nested entities tree Advanced yet easy to use input handling with InputHandler component ---------- Hi! Today i'll start series of tutorials about making games with Oxygen Core game engine. Especially for that series i've created Oxygen Playground and Embeddable Oxygen Live Experiments services to easly create and store tutorials (feel free to play with it :D). What is Oxygen Ecosystem? It's a bunch of tools and services that helps making games with Oxygen Core game engine. What is Oxygen Core? It's an ECS (Entity-Component-System) game engine based on concept that your scene is a tree of actors (entities) that may have some behaviour (component) that will make him HIM (think of it as: entity is naked and stupid until you attach some components to it). It's goal is to focus on very rapid prototyping so making games with it is fast. You can check it out on Github: https://github.com/PsichiX/Oxygen (it's a NPM module used with webpack or any other bundler, but minified script bundle is also possible to use). Anyway, I want to stop talking about engine all tech stuff and reveal them one by one in every tutorial. Okey, so first i'll show you what you'll have learn today: Playground project: http://oxygen.experiments.psichix.io/p/BkbVmOdNz Live experiment: http://oxygen.experiments.psichix.io/BkbVmOdNz (btw. Is there a way to embed this in post? it's embeddable view of live experiments so works like movies on websites) Launch Playground project and first what you'll see is code editor on the left and live preview with console on the right. Okey, so already opened code file is your index.js file, which is your game entry point. // import engine related stuff to initialize it further. import { lazyInitialization, System, vec4 } from 'oxygen-core'; // import component so we can register it in engine. import SomeController from './SomeController'; // effortlessly initialize Oxygen engine systems. lazyInitialization({ // renderer will use canvas with id 'playground-screen'. render: { screen: 'playground-screen' }, // game data will be stored under 'playground' id. store: { id: 'playground' }, asset: { // in prior to be able to use playground project assets, we have to use it's fetch engine. fetchEngine: PLAYGROUND.fetchEngine, // we don't want to cache any assets fetched into our game. fetchOptions: { cache: 'no-store' } } }); // get instances of already registered engine systems. const { AssetSystem, RenderSystem, EntitySystem } = System.systems; // register component used by animated node (WebGL logo with 'hello' text). // if we do not register this component, game will crash when some node will try to use it. EntitySystem.registerComponent('SomeController', SomeController.factory); // change renderer clear color to dark gray. vec4.set(RenderSystem.clearColor, 0.25, 0.25, 0.25, 1); // here we make kick-start of our game: // - load first JSON asset with game config and list of used assets. // - asynchronously load all assets from config list. // - trigger event that will load and initialize scene from 'game.json' asset // (scenes and prefabs are just JSON files that describes entities tree). AssetSystem.load('json://config.json') .then(configAsset => AssetSystem.loadAll(configAsset.data.assets)) .then(() => System.events.triggerLater( 'change-scene', 'scene://game.json' )); This file, created once is mostly never changed because it's just a bootstrap code to kick-start your game. Next click on application menu (upper left icon before title) and you'll see list of project files. Here you can open editable files or create new and delete old, also download them (but if you want to download whole project just click upper right EXPORT button and zipped project will land on your HDD). If you're curious what the rest of buttons are: RUN reloads live preview to see your changes. SAVE makes sure that your changes aren't lost and SHARE will show you a bunch of links you can use to share your project/changes with other devs (SHARE works only if you previously save project). Okey, so what actually makes our logo and text scaling up and down? It's entity that uses SomeController component and that component makes scaling animation like that: import { Script } from 'oxygen-core'; // create component class that extends engine Script component (extending Script // component is a base logic component so it will save us from writing a lot of code). export default class SomeController extends Script { // propsTypes it's a place that you may want to describe serializable properties. static get propsTypes() { return { speed: 'number' // commonly used types: number, string, any. }; } // this static method will be registered into EntitySystem // and it must return new instance of requested component. static factory() { return new SomeController(); } // it's good practice to initialize all instance properties in constructor. constructor() { super(); this.speed = 1; this._phase = 0; } // onUpdate() is called on every update tick, here you put your component logic. onUpdate(deltaTime) { // get your instance entity. const { entity } = this; // move animation phase by delta time multiplied by animation speed. this._phase += deltaTime * 0.01 * this.speed; // calculate animated scale from animation phase. const v = 1 + 0.5 * Math.sin(this._phase); // apply new scale to entity. // NOTE: in most HTML5 game engines you'll encounter few scenarios: // - different entity types are just extension of base entity/node/actor; // - there are no entities and components - everything is a node; // Oxygen loves separation of entity from it's logic and prefer to make logic // as components/behaviours attached into entity so given logic may be used // by many entity types, not only one. entity.setScale(v, v); } } And the question you may ask right now: ok, but where is our scene tree, if it's not present in any of code above? Let's see! Open files list and click on game.json file to open it in editor. { "name": "root", "components": { "Camera2D": { "zoomOut": 600, "zoomMode": "keep-aspect" }, "Sprite": { "shader": "sprite-transparent.json", "width": 300, "height": 125, "xOffset": 150, "yOffset": 135, "overrideBaseTexture": "logo.png" }, "TextRenderer": { "shader": "text-outline-transparent.json", "font": "verdana.fnt", "halign": "center", "valign": "top", "color": [1, 1, 1, 1], "colorOutline": [0, 0, 0, 1], "text": "Hello, Oxygen!" }, "SomeController": { "speed": 0.1 } } } This is single entity (root) scene tree, those are mostly called "prefabs" because their simplicity makes them good for instancing complex actors on scene, but here we pretend that our logo prefab is just a scene. As you may see above, there is entity named "root" that have 4 components: Camera2D to view our scene (actually to view this entity and it's children, so it's a good practice to keep cameras in root entities), Sprite to render WebGL logo with sprite transparent shader and logo texture, TextRenderer to draw "hello" text from bitmap font with outlined text shader, and SomeController that animates our entity with given speed - as you can see, every component do it's specified job! Generally scene and prefab JSON files are the place to balance your game, and code should be only the place where you put some logic and configure it with properties in scene/prefab asset. Okey, so this is all for the first tutorial - feel free to change some values of components in game.json file and click RUN (or press ctrl+enter) to see what changed And like always: i'll be very thankful for your opinion about what i did good or bad Next tutorial: Simple entity movement
  6. Example of rts space game

    oh man, website does not load, also: Phaser.Loader - image[sun]: error loading asset from URL assets/sun/sun.png
  7. Create multiplayer games rapidly using MoBubby

    exactly what @Gio said, this is even better learning curve ;p about websockets: you can provide binary communication as option for more experienced developers, who focus on best performance
  8. [WIP] Silone.Online MMO TBS

    good idea: provide "play as guest" option with in-game button "ok, i'm ready to register" - it's good for people that don't want to reguster unless game feels really good for them
  9. Create multiplayer games rapidly using MoBubby

    websockets can use binary messages, which are smaller and faster to transfer.
  10. [Phaser] Neon Dunk is here

    i recommend you put some protection that checks if your game is running from your server and display anti-theft message. or better, troll that user with redirection to porn website. they should be banned from app store pretty soon
  11. popsic.io Alpha

    for me it lags as well :/ latest chrome, galaxy s7
  12. Operation: SoulHunter

    I've updated tech description, but i really don't know what more information should be there - about tech i want to write separated article, because it's too complicated to spam this topic with it
  13. Welcome!

    Ohayo! I've found this forum by accident and i love that html5 games have that big community! I'll do my best to share my knowledge and my work and learn from you guys! ^^
  14. Ohayo! Description: Currently in my free time i'm working on crysis-like 2D shooter game prototype about aliens invasion (events between Crysis 2 and Crysis 3) and specialy trained humans in new version of nanosuit (less power of Prophet suit but more soldiers gets it). It will be multiplayer arena shooter, but for now i'm focusing on singleplayer training mission gameplay, and after i complete with fixing prototype with players feedback i'll start working on the core of the game which is multiplayer arena part. Game works smoothly on Google Chrome (game optimisation for Firefox is on future todo list) and was designed for gamepads (PlayStation and Xbox), but you can play with mouse and keyboard too. Right now aliens are very strong so gameplay is focused on tactics and hide&seek more than just running around and shooting everything you see ;P It runs on mobile too but there is no virtual controls (it's on future todo list) so don't ask me for it now Right now i'm asking you guys to tell me how does controls feels and how much time you had to play to destroy all 4 alien bases (big green dots on minimap, you can kill them only with 2 grenade shots per base :P). Play Online (please, use Chrome): http://soulhunter.psichix.io Tech used: - Oxygen Core (my html5 game engine toolset based on best Unity and Godot concepts mixed with latest best html5 apis); - ES7 with babel transpiler; - Webpack for game bundling and live development; I don't know what i should say more about techs, because everything that is there, it is those 3 things If you're interrested in something more about tech, and i will be happy to tell everything you ask ------ BTW. I want to publish topic/article about game engine used here - which subforum should i use to place that kind of topic?