LEWD as a game engine, to make other games, pt.3

The final part: how you might be able to use the engine for your own game

Basically, I want it to be about as easy as setting up WordPress for someone to get their own story driven game running.
By the same token, I want it to be easy for others to play those games. Just visit a website and play, no downloading anything, and especially not depending on another program.

Forgive the rambling-ness again for this post. It’s another that I’m writing in one sitting, and I’ve been rather sick for weeks. -_-

To be clear, I’m just making this for me and my game. What follows is sort of a pipe dream that I can definitely make happen if there support is there, and it’s what people want.
I’ve made this engine so open ended because I don’t know what sort of good ideas the supporters of it will come up with for me to include that I haven’t already thought of. I don’t LEWD to be limited by engine limitations. That’s the same thing that makes it something that would be perfect to make a sort of “Web RPG/Adventure Maker” suite.

I took a cursory look around at all things to make games like this. I knew all along that I wanted to make my own engine, because I didn’t want to be limited by what another engine could be done, but it’s good to look around and see what you can learn from others work.
Then I just sort of… decided to forget all about what I saw and start from a blank slate. What I’m saying is that I don’t like them, and decided to rethink how to do it better from a clean slate.

If this is successful, and there is support for this, and people want to make their own story-based web games, I think it should work like WordPress, a forum, Tumblr, and so on.
What I mean is that, like with Tumblr, you have your own private tools you have access to which you use to post your content. You can do things like add tags as well. Then you can style your page to have it displayed differently to people.
Wordpress is a better example, since it has a lot more plugins with what the server can do, but I’m using Tumblr there since more are familiar with it.
This engine works with the same concept. You could set up your game, and you already have a bare-bones version of it running just like LEWD, just without content, or just with a tiny bit of example content. When you set up WordPress, you have a basic blog with nothing in it. Then you have tools to add content, and you have plugins that change what you can deliver, but mostly plugins that change how the user sees it.
There could be client-side plugins which restyle it to be a 2D fighter, a Visual Novel, a 2D RPG, or whatever. They would still pull the same content but the plugins would make it display differently. It also makes it easy to add multiplayer to games that wouldn’t usually have it, though that’s not a requirement to include, of course.
I imagine there could be things like “interactive webcomics”, where instead simply going from the start to end, you can have something branched out.
MU*(MUDs, MUCKs, MUSHs, etc) can be rewritten, having a much better accessibility and more features.
Basically anything you want to make, where you want to keep track of player(s) choices, and deliver tailored content as efficiently as possible, with easy to use tools to add said content.

Further, this is all written in Javascript – the language used for every webpage on the entirety of the modern web. You learn how to do modifications to your game on the engine, and it’s the same exact knowledge used to make plugins for any other web app, or web pages yourself. It’s not some special language you learn just to use the engine yourself.

I’m talking about a working game, accessible instantly by players in the browser, in a day. I figured that’s what I’d do here.  I’ll make a new type of game, on the same engine, in less than a day. (Okay, actually 2 days since being sick made me sleepy, and writing this blog takes 10x longer than the programming takes. D:)

Setting up a server

For me, this part is already done. But basically how this would work is similar to installing blog or forum software on a server with php and mysql. You just upload the files to your server, and run an install script.
There is one extra part though, and that’s setting up the game server, and making sure you have an SQL db on your server that is facing the internet with a user account on it that gives access to the DB it uses. Initially I’ll have to set these servers up for people manually, and it’ll probably be done within 24 hours for each instance. If it becomes popular, I’ll try and get some automated system to do it set up, but these aren’t your run of the mill server setups.
This means you don’t need a dedicated server or VPS. A cheap shared host is fine. The game server that does all the real-time stuff runs on a separate server of mine.

The game itself runs on the server. All the items you gain and use, the triggering of Events, everything. It then sends JSON to the client for the client to build an interface and feedback from. This makes it so people can’t cheat, and keeps everything consistent between players. It also makes it so content is trickled to the player, instead of needing to download everything initially.

First steps, setting up the map
makingmap

With a server set up, the first thing you’d do is create a new map. Here I just made a PNG in photoshop, but I will have a tool to draw and set them up completely in the browser soon. This takes as long as it takes to plot down some dots of various color, so a few minutes.

I decided I’d make a Visual Novel styled game. You’ll move from left to right. But you wouldn’t want something just THAT basic, so I put a fork in the road as well. With the map set up in the tools, it’s time to get something displayed in the client. Since this is a different style game, it means new client code needs to be written to interpret the data from the server to fit.
I like to thing that there would be enough support and this will be popular enough that there will be plenty of plugins to grab instead of writing your own code, or I’ll have written those plugins myself. There’s none of those now, so here’s what writing a new interface looks like, starting with receiving that map data.

I should reiterate that this is just javascript, html, css. It’s the same programing language and markup languages that are used to make every website. There’s tons of resources on the internet to learn how to do things.
So it’s just like making your own game from scratch when you want to do something unique, but the hardest parts are already handled, and it’s easy to simple add the creative parts you want. If you’re just making a clone of LEWD but with different content, then there is already that client code to use. And any possible aforementioned plugins.

A playable demo will be at the end.

The start of client code

socket = io.connect('http://socket.playlewd.com'); //domain for game server

socket.on('connect', function(){ //done connecting
    socket.emit('get-map', 'vn'); //get the new map I made
});

socket.on('map', function(data){ //receive response for map data
   console.log(data); //data received. This outputs it the browser's console to be human readable
});

This is all the code it takes to connect and receive that new map.
The game server automatically sets up all the basic things. It has already made a Player object with the basics for a player. It doesn’t require you to set things like sex, gender, stats, or anything else you can think of, but of course you can.

Of course, something needs to be done with that data to have something playable.
The map data contains an array of pixels, and some properties like the map name, starting position, and the width of the map. I want these pixels in columns and rows, so there’s a function on the client to to get an x/y grid position from the number of pixels(tiles), current pixel position, and the length.

var mapWidth = 0;
function posToCords(i){
    return {
        'x':Math.floor(i / mapWidth), 
        'y':i % mapWidth
    };
}
function cordsToPos(cords){
    return (cords.x*mapWidth)+cords.y;
}

Really basic math, there.

<div id="background">&nbsp;</div>
<div id="story"><div>&nbsp;</div></div>
<div id="map-controls"><a>Talk</a> <a>Move Right(Hold)</a></div>
<div id="story-controls"><a>Up</a> <a>Down</a> <a>Continue</a></div>

Next, there’s some very basic html added. My personal preference is adding some bare bones html to a page, but creating most html dynamically with javascript.

#background {
    height:100%;
    overflow:hidden;
    position:relative;
}
#background .segment {
    height:100%;
    float:left;
}
#story {
    position:absolute; bottom:0;
    height:250px; width: 100%;
}
#story .inner {
    margin:0 8px 8px; padding:12px;
    border:1px solid black;
    height:210px;
}

Then is a bit of basic CSS styling for those elements.

var $map = $('#background');
var $mapControls = $('#map-controls');
var $story = $('#story');
var $storyControls = $('#story-controls');

Then setting variables in javascript to cache those selectors, instead of reselected them each time they’re needed.

var mapWidth = 0;
var mapHeight = 0;
var pixels = [];
socket.on('map', function(data){ //receive response for map data
    console.log(data); //data received
    mapWidth = data.props.width;
    mapHeight = pixels.length / mapWidth;
    pixels = data.pixels;
    mapFromPixels();
});

function mapFromPixels(){
    var $map = $('#background');
    $map.empty();
    $map.width( mapWidth*mapSize );
    for( var i = 0; i < mapWidth; i++ ){
        var $segment = $('<div />');
        $segment.css('width', mapSize+'px');
        var column = [];
        for( var ir = 0; ir < mapHeight; ir++ ){
            var pixel = pixels[ cordsToPos({'x':i, 'y':ir}) ];
            if( pixel[0] !== 0 || pixel[1] !== 0 || pixel[2] !== 0 || pixel[3] !== 0 ){
                column.push( {'pos':ir*mapHeight+i, 'y':ir, 'rgba':pixel} );
            }
        }
        $segment.data('pixels', column);
        console.log( $segment.data('pixels') );
        $segment.appendTo( $map );
    }
}

Lastly, for this section, is adjusting the code to include a “map building” function, which builds a bunch of horizontal segments. I’m also going to include data on the vertical in each segment, which will become useful later. In the future, I’m going to make a proper client API which will already have some of these functions. Right now, the LEWD client needs a rewrite.

And finally, we get something this:
maprender1
That’s basically what you’d need in a VN. There’s a box at the bottom for the story, over the background. The background is yeah, just white, since I don’t have art for this. :3
The blue bar is the web inspector tools in the browser highlighting one of the “segments” of the map, which is otherwise invisible. The code also includes data added to each segment to list all the Areas in it. That gives all the data needed to see if it’s a moveable spot, and there could be things above or below that are like a cave or whatever. In this case, there’s that fork in the road putting two Areas on the segments past that fork.

That’s a start, but obviously we need some movement, and indication of the player, and so on…

var $player = $('<div id="player-mob">Your char.<br />Use your imagination D:</div>');
function drawPlayer(startPos){
    $player.appendTo( $map );
}

$mapControls.find('.right').bind('click', function(){
    $player.animate({'left':'+=40'}, 250);
});

I added the drawPlayer(…) function call to the end of the socket.on(‘map’) event as well as well as some other styling stuff. The results are here: http://jsfiddle.net/Ev8nq/
And it looks like this:
maprender2
You can move to the right to go through the area, but that’s it. It’s missing the communication with the server to relay the movement. There’s also no content there.
Yeah, of course it’d be nice to have some character that actually walks when it’s moved, but you have to use your imagination here since I don’t have that artwork~

Before hooking in the movement to the server, I’ll add some content.
content1

I’ll start with a super bare-bones encounter in the second Area to test with, bound to that second tile with the bit of blue. Oh yeah, you’ll notice that the tools are a bit different than since the last posts.

Back to the client code, I’m going to have the client and server start communicating.

var $player = $('<div id="player-mob">Your char.<br />Use your imagination D:</div>');
function drawPlayer(startPos){
    curCords = posToCords(startPos);
    $player.css('left', mapSize*curCords.x+'px');
    $player.appendTo( $map );
}

First, I’m editing the function that places that little player box. the ‘map’ event supplied the starting position, and my posToCoords function got the x,y coords, so it’s a matter of just offsetting where it appears by that mapSize amount that was set. In this case, it’s offsetting it 400pixels, so it starts at the beginning of the 3rd segment, corresponding with that shot of the map at the start.

var moveTicker;
$mapControls.find('.right').bind('mousedown', function(){
    moveTicker = setInterval(function(){
        $player.animate({'left':'+=10'}, 100, "linear", function(){ //animation end
            var posLeft = $player.position().left;
            var docLeft = $player.offset().left;
            var x = Math.floor( posLeft / mapSize );
            if( x > curCords.x ){
                console.log('moving right');
                socket.emit('d'); //move right. wasd; up, left, down, right; get it?
            }
        });
    }, 100);
});
$mapControls.find('.right').bind('mouseup', function(){
    clearInterval(moveTicker);
});

socket.on('move-player', function(pos){ //Will need this to listen for when the server moves the player.
    curCords = posToCords(pos);
    console.log('move-player', pos, posToCords(pos));
});

Here the movement function has been changed. x > curCords.x is checking if the current horizontal movement is pushing it out past the current segment of the map, and into the next area. When that happens, it sends the “d” event to the server which is short for telling it to move the player to the right. “w”, “a”, “s”, “d” are the basic movement commands, though that’s just one way a player can be moved. I also changed it to be a smooth movement as long as the button is held.
So now it’s moving on the client, and also updating that to the server so it knows they’re moving.  It does this just when a new segment is entered into. Sending a position update every pixel or something would be silly.

I also added in the “move-player” event. This is what the server sends back when it’s confirmed a change of player position, which can happen for a lot more reasons than the client simply requesting it to be done. Here I update the current position, which the movement function is using. Later on it’ll need updating for the fork in the road part, but I’ll get to that when it’s apt later down in this post.
When the client requests movement, it might not be allowed for a variety of reasons. “move-player” is only sent back when they were allowed to move.

Html changed to have story controls start hidden:
<div id="story-controls" style="visibility:hidden;"><a>Up</a> <a>Down</a> <a>Continue</a></div>

Javascript:
socket.on('event-start', function(data){
    $storyControls.css('visibility', 'visible');
    $mapControls.find('.right').trigger('mouseup');
    $mapControls.css('visibility', 'hidden');

    console.log('event-start', data);
});
socket.on('event-change-scene', function(data){
    console.log('event-change-scene', data);
    var writing = data.scene.writing;
    $story.empty();
    $('<p>'+writing+'</p>').appendTo( $story );
    var $options = $('<ul />').appendTo( $story );
    _.each(data.scene.options, function(option, i){
        $('<li><a>'+option.s+'</a></li>').data('index', i).appendTo( $options );
    });
    $options.find('li:first').trigger('mouseenter');
});
$story.on('mouseenter', '.options li', function(){
    var $this = $(this);
    $story.find('li').removeClass('focused');
    $this.addClass('focused');
});
$story.on('click', '.options li', function(){
    socket.emit('event-option', $(this).data('index'));
});
$storyControls.find('.confirm').bind('click', function(){
    $story.find('li.focused').click();
});
$storyControls.find('.up').bind('click', function(){
    var $curFocus = $story.find('li.focused');
    var nexti = $curFocus.index();
    var len = $story.find('.options li').length;
    nexti = nexti-1 < 0 ? len-1 : nexti-1;
    $story.find('.options li:eq('+nexti+')').trigger('mouseenter');
});
$storyControls.find('.down').bind('click', function(){
    var $curFocus = $story.find('li.focused');
    var nexti = $curFocus.index();
    var len = $story.find('.options li').length;
    nexti = nexti+1 > len-1 ? 0 : nexti+1;
    $story.find('.options li:eq('+nexti+')').trigger('mouseenter');
});
socket.on('event-end', function(){
    console.log( 'event-end' );
    $story.empty();
    $storyControls.css('visibility', 'hidden');
    $mapControls.css('visibility', 'visible');
});

Lastly, now that there is an Event at that location it now sends that data to the client when they move there. Here I’ve added listeners for that, and I have the movement stop when one pops up, and the information in it displays. The button for movement gets hidden as well, while the buttons for scene navigation show. I also got all those buttons working, even though there aren’t options to cycle through yet.
Here it is, a working example here: http://jsfiddle.net/abLGk/
But there’s still two glaring issues.
One is the whole how it doesn’t continue further to that fork in the road part, and deal with that. And the screen needs to move to keep up with the player moving past it.
The other big one is that this doesn’t look much like Visual Novel encounters. Usually they have a series of text coming in one line at a time, and it shows the portrait of your character and the NPCs for whichever is talking, before finally giving the options.  Not the text and options all at once like there is now. Next I’m going to fix that part, starting by adding some more writing to test that one.

Setting up more content to test the upcoming writing parser
content2

Besides adding another option, I’ve put in some lines. They’re meaningless, but in a moment you’ll see how I’m going to parse those to make that effect I described earlier. Right now it’s just going to be dumping all those different lines in the box.

HTML:
<div id="story"><div class="player-portrait">Player Portrait</div><div class="npc-portrait">NPC Portrait</div><div class="inner">&nbsp;</div></div>

CSS:
#story .player-portrait, #story .npc-portrait {
    height:250px;
    width:175px;
    background:#7799AA;
    position:absolute;
    top:-250px;
    left:8px;
    display:none;
}
#story .npc-portrait {
    background:#CC6611;
    left:auto;
    right:8px;
}

First I’ve put in some html for the portraits, and some styling so they appear above the box with  the writing, to the left and right. Obviously it’d be better to have pictures there, but I don’t have those so it’s just colored boxes again.

 var writingPos = 0;
var splitWriting = [""];
var $sceneOptions;
socket.on('event-change-scene', function(data){
    console.log('event-change-scene', data);
    var writing = data.scene.writing;
    splitWriting = writing.split('\n\n');
    writingPos = 0;
    $storyControls.find('.confirm').trigger('click');

    $options = $('<ul />').hide().appendTo( $story );
    _.each(data.scene.options, function(option, i){
        $('<li><a>'+option.s+'</a></li>').data('index', i).appendTo( $options );
    });
    $options.find('li:first').trigger('mouseenter');
});
$storyControls.find('.confirm').bind('click', function(){
    if( writingPos >= splitWriting.length ){
        $story.find('li.focused').click();
        $story.find('.options').remove();
    } else {
        $storyControls.find('.up, .down').css('visibility', 'hidden');
        $story.find('.writing').remove();
        var line = splitWriting[writingPos];
        var speakerIndex = line.indexOf(':');
        var speaker = line.slice(speakerIndex-3, speakerIndex);
        line = line.slice(speakerIndex+1);
        $('<p>'+line+'</p>').prependTo( $story );
        $('.player-portrait, .npc-portrait').hide();
        if( speaker === 'you' ){
            $('.player-portrait').show();
        } else if( speaker === 'npc' ){
            $('.npc-portrait').show();
        }
        if( writingPos == splitWriting.length-1 ){
            $storyControls.find('.up, .down').css('visibility', 'inherit');
            $story.find('.options').show();
        }
        writingPos += 1;
    }
});

Here’s the Javascript that makes that first part work.
Now it’s taking that block of text and spliting it on the double new lines, “\n\n”, and seeing if it starts with a “you:” or “npc:” to decide which portrait to show.  If it started with neither, it’d show neither.
The “continue” button’s functionality is also changed to cycling through the lines, or confirming the choices once it gets to the end.

Though, shouldn’t that text only come up if you hit a “talk” button? And shouldn’t they show up on the world?

socket.on('explore', function(data){
    $mapControls.find('.confirm').css('visibility', 'inherit');
});
socket.on('move-player', function(pos){
    curCords = posToCords(pos);
    console.log('move-player', pos, posToCords(pos));
    $mapControls.find('.confirm').css('visibility', 'hidden');
});
$mapControls.find('.confirm').bind('click', function(){
    socket.emit('explore');
});

In the tools, I went in an checked “Explore Only”. This stops the Event from automatically being rolled when someone enters an area. Instead, the “explore” event is sent to the player to say there is something explorable there. “explore” can be emitted right back and then it will roll the Events there. I’m also turning that “Talk” button off and on here to indicate that someone can be talked to.

           if( pixel[2] !== 0 ){ //spot where the NPC is
                var $npc = $('<div>test</div>');
                $npc.css('bottom', '+='+(10+(ir-5)*25)).css('left', (mapSize/2)-($npc.width()/2));
                $npc.appendTo( $segment );
            }

This is a really a really hackish way to do it, but I just put that code in the inner loop of the map creation to throw in an NPC where there are tiles that have any blue. Really there would need to be something that gives them an appropriate picture.

I think that’s working perfect, but there needs to be a bit more if it, I need to deal with the fork in the road, and the screen needs to move with the player.

$mapControls.find('.right').bind('mousedown', function(){
    moveTicker = setInterval(function(){
        $player.animate({'left':'+=10'}, 100, "linear", function(){ //animation end
            var posLeft = $player.position().left;
            var docLeft = $player.offset().left;
            if( docLeft > winWidth - 200 ){
                $map.animate({'marginLeft': '-='+winWidth/2});
            }
            var x = Math.floor( posLeft / mapSize );
            if( x > curCords.x ){
                socket.emit('d'); //move right. wasd; up, left, down, right; get it?
            }
        });
    }, 100);
});

I simply edit the movement function to check if the position of that player element is moving close to the edge of the window, and shift the map half its width to the left if it is.

npctalking
The result of all that.
This stage of it is here: http://jsfiddle.net/HR5a4/

We still need that fork in the roadcontent3

Again I’ve gone back to the tools to add that bit of content. I also went ahead and added some “this is the end” events to stop the player at those. If there was more content, they’d be loading in another map or something.

With what is on the client already, this will already pop up with that text, and then the options.
What needs to be added on the client, is something to handle the teleporting. “teleportTo” will forcibly move the player, and send a “move-player” event with the new location. The numbers correspond to the green points of the map you saw earlier. teleportTo can also go to a non-green tile with something like 0,0,180,255 supplied instead of a single number, which would teleport them to that NPC near the start.

Handling the “move-player” event when teleported.

 socket.on('move-player', function(pos){
    console.log('move-player old', curCords );
    var x = Math.floor( $player.position().left / mapSize )
    console.log('move-player x', x);
    var newCords = posToCords(pos);
    console.log('move-player new', pos, posToCords(pos));

    if( curCords.y !== newCords.y ){
        var change = curCords.y - newCords.y;
        $player.animate({'bottom': '+='+change*30}, { duration: 2000, queue: false });
    }
    if( x !== newCords.x ){
        var change = (newCords.x - x ) * 200;
        var dur = change*10;
        $player.animate({'left': '+='+change}, { duration: dur, queue: false });
    }

    curCords = newCords;
    $mapControls.find('.confirm').css('visibility', 'hidden');
});

When teleported, there’s going to be some big inconsistency with where the player seems to be on the client, and where the server says they should be. So the curCords that was set from the last movement is compared with the new position converted to coordinates.
It’s important to not have anything happen when the player is just smoothly moving from one tile to the next, as it could make some “rubber banding”.
The middle part of this code detects these inconsistencies. It moves the player higher/lower on that split path. It will also move them forward. In the case of them having a very very slow connection, it will also “rubber band” them back when needed.

This “final” part is here: http://jsfiddle.net/rjW9U/

Well. All the things basically work.
If there were more maps, and there was an Event that teleported the player to another map, it would build that new map and place the player object here.  Like, say a town to enter off one of those paths, or a dungeon to branch off into then return back to the map from.
It could use some combat, like some 2d hack and slash things to fight, which would need to be all client side. Or there could be Events with random encounters turn based combat, but I’m not going to do that.

I did say I’d make a game in a day, but this part only took less than 2 hours.  It’s the writing all this about it that took all the time.
So I figure that I may as well spruce it up to look a little nicer. Right now, it’s hard to get an idea of movement without a background.
I’m not going to explain that next part, though, as this post is already so huge. The explaining is a lot harder than the doing, too. :/

Well, that looks alright. Now there’s a better sense of movement. Viewing the source, you can see it’s largely the same.
I just incorporated Three.js and its CSS3D renderer.
Three.js is a 3d graphics library. It’s not a game engine by itself, but it can be combined with the game server to make a game easily.

Ideally I’d have an actual road there, with the player moving along a spline instead of such a straight robotic line. Ideally there’d be artwork for the characters, too, as well as trees or something in the background and other art. But yeah, that’d take a lot more time just for an example. This should be enough to give an idea.
Perhaps in the future I’ll make a proper VN plugin. It just depends a bit on what people want, just how much support/demand there is in general.

 

If things go well, I want to have something where anyone can have a “test” version of it for free.
Basically, at that point someone would just be able to go to a web page to make a new “test game”, have access in a minute, and there they can play around in the tools and have their own game right there to test out. This is like a sandbox, just to see how easy it is to use. This is just like how forums software websites tend to have a sandbox page to mess around with, to see how you like it before going through setting up a server for your own to work on and customize.
It’d also be nice to have some sort of shared hosting setup so people can make simple games cheaply, or for free, without them needing their own game server. There would be limits on not having completely custom game server coding on something like that, though, since that code if done wrong can make lag and tax the server.
This would work similarly to something like roll20 or tinychat where you just fill out a short form, and get a URL for your instance right away.

 

I really want to kill things like Byond. In a good way! There just needs to be something better. Byond is just far too inefficient, and convoluted. Many Byond games can only handle 2 or 3 dozen players on a $50/month server. LEWD can easily handle 30 times more for that, and without all the lag.
People shouldn’t need any programming experience just to make content. They should have tools to add content like LEWD has.
I also want to do away with the whole needing a 300mb download off from a file repo before you get to try a game when it’s something like RAGS, as well as needing a game player application for it to run, and to keep having to reload it when it’s updated. It’s so much easier to just visit a site, and get the game playing right there with no software required except for a modern web browser.
And for MU* games, people can make the same content a lot easier, with a more modern interface for the players. That is basically what LEWD is, a MU* but running in the browser, and with a more modern interface and controls.

Bookmark the permalink.