niedziela, 20 września 2015

Alien Invasion, v. 0.8, albo niżej...


Trwa nauka javascriptu i canvas html5...
Canvas to dość ciekawe coś... daje obsługę grafiki, więc można się skupić na kodowaniu gameplaya...
Załączony kod jest w zasadzie prototypem, co wiecej, wiele funkcji dałoby się oczywiście przerysować inaczej, żeby jeszcze skrócić program o parę wierszy.
Ale oczywiście programowanie szło liniowo, więc i wszelkie niedoskonałości tegoż szły wraz z nim...

Poniżej kod wrzucony do jednego pliku:



<!DOCTYPE html>
<html>
<head>
    <title>Alien Invasion v.0.8</title>
    <!--
    <link rel="stylesheet" type="text/css" href="css/style.css">
    -->
    <style type="text/css">
        body, html { margin: 0; padding: 0; background-color: #222; }

        .text-center {    
            margin: auto;    
            text-align: center;    
        }

        .valign {
            position: relative;
            top: 50%;
            -webkit-transform: translateY(-50%);
            -ms-transform: translateY(-50%);
            transform: translateY(-50%);
        }

        #content {
            margin-top: 20px;

            padding: 0;
        }

        #canvas {
            border: 2px solid #555;    
            background-color: #000;
        }



    </style>
</head>
<body>    
    <div id="content" class="text-center">
        <canvas id="canvas" class="valing" width="1200" height="600"></canvas>
    </div>
    
    <!--
    <script type="text/javascript" src="js/script.js"></script>
-->

<script type="text/javascript">
    var canvas = document.getElementById("canvas");
    var surface = canvas.getContext("2d");
    surface.fillStyle = "rgba(255,255,255,1)";

//fonts
surface.font = "normal bold 20px Helvetica";
surface.fillStyle = "#FFF";
surface.textBaseline = "top";

//var image = new Image();
// image.addEventListener("load", loadHandler, false);
// image.src="img/alieninvasion.png";

var rockets = [];
var aliens = [];
var blasts = [];
var blastRange = 20;    //default rocket blast range
var missilesFired = 0;
var aliensKilled = 0;
var alienKilledRatio = 0;
var humansKilled = 0;
var humansKilledAtBlast = Math.round(Math.random()*(500-100)+100);

var mousePos;

var MovableObjectClass = {
    x:0,
    y:0,
    velocity: 0,
    xTarget:0,
    yTarget:0,
    xVect:0,
    yVect:0,
    dVect:0,
    vJedn:0,
    vxSpeed:0,
    vySpeed:0,
    blastRadius: blastRange,

    speedVectorCount: function(targetX, targetY) {
        this.xTarget = targetX;//mousePos.x;
        this.yTarget = targetY;//mousePos.y;
        this.countActualPosition();
        this.countDistanceToTarget();
        this.vxSpeed = ((this.xVect*this.velocity)/this.dVect);
        this.vySpeed = ((this.yVect*this.velocity)/this.dVect);
    },
    
    countActualPosition: function() {
        this.xVect = this.xTarget - this.x;
        this.yVect = this.yTarget - this.y;
    },

    countDistanceToTarget: function() {
        this.dVect = Math.sqrt(this.xVect*this.xVect+this.yVect*this.yVect); //dlugosc wektora
    },

    explosionRangeCheck: function (targetX, targetY) {
        var distX = targetX-this.x;
        var distY = targetY-this.y;
        return Math.sqrt(distX*distX+distY*distY); //dlugosc wektora wybuchu
    },

    draw: function (w,h, drawColor) {
        this.x+=this.vxSpeed;
        this.y+=this.vySpeed;
        surface.fillStyle = drawColor; //"rgba(255,255,255,1)";
        surface.fillRect(this.x-w, this.y-h, 2*w,2*h);
    }
}

var BlastObjectClass = {
    x:0,
    y:0,
    blastRadius: blastRange,
    blastColor: "#FF0"
}

window.addEventListener("load", gameStart,false);
canvas.addEventListener("mousedown", mouseDownHandler,false);
canvas.addEventListener("mousemove", function(evt) {
        //var mousePos = getMousePos(canvas, evt);
        mousePos = getMousePos(canvas, evt);
    }, false);

function getMousePos(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
            // x: evt.clientX - rect.left,
            // y: evt.clientY - rect.top
            x: evt.clientX - (canvas.offsetLeft - window.pageXOffset),
            y: evt.clientY - (canvas.offsetTop - window.pageYOffset)
        };
    }

    function mouseDownHandler() {
        rocketFire();
    //console.log(rockets);
    //update();
}

function gameStart() {
    //function starts aliens cycle & game render cycle
    aliensFire();
    update();
}

function update(){
    window.requestAnimationFrame(update, canvas);    
    render();
}

function render() {
    surface.fillStyle = "rgba(0,0,0,1)";
    surface.clearRect(0, 0, canvas.width, canvas.height);
    surface.fillStyle = "rgba(255,255,255,1)";
    surface.fillRect(Math.round(canvas.width/2)-2, canvas.height-5, 4,5);

    aliensDraw();
    rocketsDraw();
    blastDraw();
    statsDraw();

}

function statsDraw() {
    //Display the message text above the square
    if (missilesFired!=0) {
        alienKilledRatio = (( (aliensKilled*100) / missilesFired)).toFixed(2); 
    }
    
    surface.fillStyle = "rgba(255,255,255,1)";
    surface.fillText("Humans lost: "+humansKilled, 10, 10);
    surface.fillText("Shots: "+missilesFired, 10, 30);
    surface.fillText("Ratio: "+ alienKilledRatio + "%", 10, 50);
    surface.fillText("Kills: "+aliensKilled, 10, 70);
}

function rocketFire(){
    var rocket = Object.create(MovableObjectClass);
    missilesFired++;
    rocket.x = Math.round(canvas.width/2);
    rocket.y = canvas.height-6;
    rocket.velocity = 2;
    rocket.speedVectorCount(mousePos.x, mousePos.y);
    rockets.push(rocket);    
}

function rocketsDraw() {
    for (var x=0; x<rockets.length; x++) {
        rockets[x].draw(1,1, "rgba(255,255,0,1)");
        rockets[x].countActualPosition();
        rockets[x].countDistanceToTarget();                
        if (rockets[x].dVect<3) {
            blastFire(rockets[x].x, rockets[x].y, "#550", 20);  //rysuj wybuch na pozycji
            //sprawdz miejsce wybuchu na obcych
            for (var i=0; i<aliens.length; i++) {
                //sprawdz odleglosc obiektow
                var blast = rockets[x].explosionRangeCheck(aliens[i].x, aliens[i].y);
                //jesli w zasiegu, wytnij aliena z tablicy
                if (blast<rockets[x].blastRadius) {
                    aliens.splice(i,1);
                    aliensKilled++;
                }
            }
            rockets.splice(x,1); //wytnij rakiete po wybuchu
        }
    }   
}

function blastFire(xBlast,yBlast,blCol,blRad) {
    if (blRad === undefined) blRad = blastRange;
    var blast = Object.create(BlastObjectClass);
    blast.x = xBlast;
    blast.y = yBlast;
    blast.blastColor = blCol;
    blast.blastRadius = blRad;
    blasts.push(blast);
    
    window.setTimeout(blastRemove, 100);
}

function blastRemove() {    
    blasts.splice(0,1);
    console.log(blasts);
}

function blastDraw() {    
    for (var j=0; j<blasts.length; j++) {        
        surface.beginPath();
        surface.arc(blasts[j].x,blasts[j].y,blasts[j].blastRadius,0,2*Math.PI, false);
        //surface.fillStyle = '#110';   //wypelnienie
        //surface.fill();        
        surface.lineWidth = 1;
        surface.strokeStyle = blasts[j].blastColor;//'#550';
        surface.stroke();
    }    
}



function aliensFire() {
    window.setTimeout(aliensFire, (Math.random() * (4000 - 1000) + 100));
    var alien = Object.create(MovableObjectClass);
    alien.x = Math.round(Math.random() * canvas.width);
    //alien.velocity = Math.round(Math.random() * (1-0.5)+0.5);
    alien.velocity = 0.3;
    alien.xTarget = Math.round(Math.random() * (canvas.width-100)+100);
    alien.yTarget = canvas.height;
    alien.speedVectorCount(alien.xTarget, alien.yTarget);
    //console.log("aliens vector! " + alien.dVect);
    aliens.push(alien);
}

function aliensDraw() {
    for (var x=0; x<aliens.length; x++) {
        aliens[x].draw(2,2, "rgba(0,255,0,1)");
        aliens[x].countActualPosition();
        aliens[x].countDistanceToTarget();                
        if (aliens[x].dVect<3) {            
            blastFire(aliens[x].x, aliens[x].y, "#F00", 100);  //rysuj wybuch na pozycji
            //humans killed
            humansKilled+=humansKilledAtBlast;

            aliens.splice(x,1);            
        }
    }      
}
</script>    
</body>
</html>

G

piątek, 18 września 2015

Langton's Ant w javascript i canvas


Tym razem zabrałem się za mrówkę Langtona. Prosty relatywnie pomysł pomaga początkującym programistom opanować kilka elementów, m.in. poruszanie się w przestrzeni 2D i kontrolowanie kierunku ruchu... Przy okazji pozwala zaobserwować ciekawy efekt matematyczny...

Poniżej kod jednoplikowy:

mrowka.html



<!DOCTYPE html>
<html>
<head>
    <title>Langton's Ant</title>
    <style type="text/css">
        body, html { margin: 0; padding: 0; height: 100%; width: 100%; }
        #content { position: relative; height: 100%; }
        #canvas { border: 1px solid black; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); }
    </style>
</head>
<body>
    <div id="content">
        <canvas id="canvas" width="200" height="200"></canvas>
    </div>

<script type="text/javascript">
var canvas = document.getElementById("canvas");
var surface = canvas.getContext("2d");
surface.fillStyle = "rgba(0,0,0,1)";

var AntClass = {
    x: 0,
    y: 0,
    kierunek: 1,

    turnLeft: function() {
        this.kierunek--;
        if (this.kierunek<1) this.kierunek = 4;
        this.move(this.kierunek);
    },
    turnRight: function() {
        this.kierunek++;
        if (this.kierunek>4) this.kierunek = 1;
        this.move(this.kierunek);
    },
    move: function(dir) {
        switch(dir) {
            case 1:
                ant.y--;
                break;
            case 2:
                ant.x++;
                break;
            case 3:
                ant.y++;
                break;
            case 4:
                ant.x--;
                break;
            default:
        }
    }
}   //class end

var ant = Object.create(AntClass);
ant.x = Math.round(canvas.width/2);
ant.y = Math.round(canvas.height/2);
ant.kierunek = 1;

window.addEventListener("load", goAnt, false);

function goAnt() {
    var color = surface.getImageData(ant.x, ant.y, 1, 1);
    var red = color.data[0];

    if (red >100) {
        surface.fillStyle = "rgba(0,0,0,1)";
        surface.fillRect(ant.x, ant.y, 1,1);
        ant.turnLeft();
    } else {
        surface.fillStyle = "rgba(255,255,255,1)";
        surface.fillRect(ant.x, ant.y, 1,1);
        ant.turnRight();
    }

    //timer
    window.setTimeout(goAnt, 1);

}
</script>
</body>
</html>

----------------
G

czwartek, 3 września 2015

JS Pirates

W ramach poznawania javascriptu wycisnąłem z siebie w trzy dni prostą gierkę casualową na przeglądarkę. Kod jest na githubie. Nieco nieogarnięty, bo projekt powstawał bez wstępnego planowania. Ale działa. Jako tako… 



img
   
    


html

<!DOCTYPE html>
<html>
<head>
    <meta charser="utf-8" />

    <title>Pirates</title>

    <link rel="stylesheet" type="text/css" href="css/style.css" />

</head>
<body>
    <div id="gameBoard">
        <div id="content">        
            <div id="sea"></div>
            <div id="panel">
                <div class="valign">
                    <div id="tradeMsg"></div>                    
                    <button id="sailToSea">Leave Island</button>    
                </div>
            </div>
        </div>
        <div id="gui">            
            <div id="stats" class="vmsg"></div>            
            <div id="msg" class="vmsg"></div>
        </div>
    </div>
    <script type="text/javascript" src="js/script.js"></script>
</body>
    </html>

css

body, html { margin: 0px; padding: 0px; font-size: 25px; }

button {
    min-height: 50px;
    min-width: 100px;
    border-radius: 5px;
    background-color: red;
    color: white;
    font-weight: bold;
    border: 1px solid black;
    -webkit-user-select: none;  /* Chrome all / Safari all */
    -moz-user-select: none;     /* Firefox all */
    -ms-user-select: none;      /* IE 10+ */
    user-select: none; 
}

.cell {    
    width: 80px;
    height: 80px;    
    display: block;
    position: absolute;        
    
}

.text-center {    
    margin: auto;    
    text-align: center;    
}


#gameBoard {
    width: 800px;    
    margin: 10px auto;    
    border: 1px solid #000;
    position: relative;    
}

#content { 
    height: 800px;    
    position: relative;
}

#sea { 
    width: 100%;
    height: 100%;    
    background-color: #6C82BF;        
    position: absolute;
}

#panel { 
    width: 100%;
    height: 100%;    
    background-color: rgba(0,0,0,0.8);        
    position: absolute;
    text-align: center;
}

.valign {
    position: relative;
    top: 50%;
    -webkit-transform: translateY(-50%);
    -ms-transform: translateY(-50%);
    transform: translateY(-50%);
}

#tradeMsg {
    max-width: 50%;
    margin:30px auto;
    padding: 20px;
    color: white;
    border: 1px solid white;
    border-radius: 20px;

}

#tradeMsg > img, span {
    vertical-align: middle;
}

#gui {
    width: 100%;    
    position: relative;        
    background-color: #FFB440;
    text-align: center;
}

#stats {
    border-top: 1px solid black;
    width: 100%;
    background-color: rgba(0,0,0,0.1);    
    padding: 10px 0;
}

#msg {
    text-align: center;    
    width: 100%;
    background-color: rgba(232,182,102,0.1);
    padding: 10px 0;
}

.vmsg > img {
    vertical-align: middle;
}

.vmsg > span {
    vertical-align: middle;
    padding-left: 10px;
    padding-right: 50px; 
}

js

//CONSTANT
var WATER = 0;
var ISLAND = 1;
var HOME = 2;
var PIRATE = 3;
var SHIP = 4;
var MONSTER = 5;
var SKULL = 6;
var SINK = 7;

var UP = 38;
var DOWN = 40;
var RIGHT = 39;
var LEFT = 37;

//arrays
var TILESIZE = 80;
var map = [
    [0,0,0,0,0,0,0,0,0,0],
    [0,1,0,0,0,0,0,0,1,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,1,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,1,0,0],
    [0,1,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,2,0,0,0,0,1,0],
    [0,0,0,0,0,0,0,0,0,0],
];
var gameObjects = [
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
    [0,0,0,0,4,0,0,0,0,0],
    [0,0,0,0,0,0,0,0,0,0],
];
var ROWS = map.length;
var COLUMNS = map[0].length;

//Variables
//start ship position
var shipRow = 8;
var shipCol = 4;

var moveShip = true;
var food = 20;
var gold = 10;
var experience = 0;
var days = 0;
var gameScore = 0; 
var gameMessage = "Use the arrow keys to sail.<br/>Your goal is to fight pirates & buy food.<br/> Sale to HQ to end game.<br/>[F5] New game";


//TAGS vel ELEMENTS
var content = document.getElementById("content");
var sea = document.getElementById("sea");
var gui = document.getElementById("gui");
var stats = document.getElementById("stats");
stats.innerHTML = "<img src='images/gold.png'><span>" + gold + "</span>" 
    + "<img src='images/food.png'><span>" + food + "</span>" 
    + "<img src='images/score.png'><span>" + experience + "</span>" 
    + "<img src='images/days.png'><span>" + days + "</span>";
var msg = document.getElementById("msg");
msg.innerHTML = gameMessage;
var panel = document.getElementById("panel");
panel.style.display = "none";
var tradeMsg = document.getElementById("tradeMsg");
var sailToSeaBtn = document.getElementById("sailToSea");
//var endTheGame = document.getElementById("endTheGame");
//endTheGame.style.display = "none";

//EventHandlers
window.addEventListener("keydown", keydownHandler, false);

//FUNCTIONS

function keydownHandler(event) {
    moveShip = false;
    switch(event.keyCode) {
        case UP:            
            if(shipRow > 0) {            
                moveShip = true;
                gameObjects[shipRow][shipCol] = 0;            
                shipRow--;            
                gameObjects[shipRow][shipCol] = SHIP;
            }
            break;
        case DOWN:
            if(shipRow < ROWS - 1) {
                moveShip = true;
                gameObjects[shipRow][shipCol] = 0;
                shipRow++;
                gameObjects[shipRow][shipCol] = SHIP;
            }
            break;
        case LEFT:
            if(shipCol > 0) {
                moveShip = true;
                gameObjects[shipRow][shipCol] = 0;
                shipCol--;
                gameObjects[shipRow][shipCol] = SHIP;
            }
            break;
        case RIGHT:
            if(shipCol < COLUMNS - 1) {
                moveShip = true;
                gameObjects[shipRow][shipCol] = 0;
                shipCol++;
                gameObjects[shipRow][shipCol] = SHIP;
            }
            break;
    };
    if (moveShip) {        
        playGame();     
        renderMap();        
    }    
}


function renderMap() {
    if(sea.hasChildNodes()) {
        for (var row = 0; row < ROWS * COLUMNS; row++) {
            sea.removeChild(sea.firstChild);
        };
    }

    for (var row = 0; row < ROWS; row++) {
        for (var col = 0; col < COLUMNS; col++) {
            var cell = document.createElement("div");
            cell.setAttribute("class","cell");
            sea.appendChild(cell);

            cell.style.top = row * TILESIZE + "px";
            cell.style.left = col * TILESIZE + "px";
            
            switch(map[row][col]) {
                case WATER:
                    cell.style.backgroundImage = "url('images/water.png')";
                    break;
                case ISLAND:
                    cell.style.backgroundImage = "url('images/island.png')";
                    break;
                case HOME:
                    cell.style.backgroundImage = "url('images/home.png')";
                    break;                
               default:
            }

            switch (gameObjects[row][col]) {
                case SHIP:
                    cell.style.backgroundImage = "url('images/ship.png')";
                    break;
                case PIRATE:
                    cell.style.backgroundImage = "url('images/pirate.png')";
                    break;
                case MONSTER:
                    cell.style.backgroundImage = "url('images/monster.png')";
                    break;
                case SKULL:
                    cell.style.backgroundImage = "url('images/skull.png')";
                    break;
                case SINK:
                    cell.style.backgroundImage = "url('images/sink.png')";
                    break;
                default:
                    break; 
            }
        }
    }
}


//where is the ship on the map and what TODO
function playGame() {    
    food--;
    //if (food==5) { printMessage("More food you need or you die!<br/>Visit an island."); }
    gameScore = (gold * experience)/2; 
    
    switch(map[shipRow][shipCol]) {
        case WATER:
            days++;
            generateEvents();
            break;
        case ISLAND:
            islandTrade();
            break;
        case HOME:
            gameMessage = "Welcome home, captain, after " + days + " days! Check your stats...<br/>You've got <strong>" + experience + "</strong> <img src='images/score.png'> total points.<br/>THE END";
            endGame();
            break;                
        default:
            break;
    }
    printStatus();
};


function generateEvents() {
    printMessage("Sail ahead, captain!");
    if (food <= 0) {
            console.log("koniec jedzenia");
        gameObjects[shipRow][shipCol]=SKULL;        
        gameMessage = "No more food.<br/>THE END";
        endGame();
        return;
    }
    var chance = Math.floor(Math.random()*100);
    if (chance<5) {
            console.log("rafy");
        gameObjects[shipRow][shipCol] = SINK;
        gameMessage = "BAD luck, captain. Reefs!!!<br/>THE END";
        endGame();
        return;
    };
    if (chance<30) {
            console.log("piraci");
        fightPirates();
        return;
    };
    if (chance<35) {
        console.log("35% - empty");
        return;
    };
    if (chance<50) {
        console.log("skarb");
        var treasure = Math.floor(Math.random()*20)+3;
        gameMessage = "GOOD luck, captain.<br/>"+ treasure +" <img src='images/gold.png'> treasure found!!!";        
        gold+=treasure;
        printMessage(gameMessage);
        
        return;
    };
    if (chance<75) {
        console.log("75%");
        return;
    };
    if (chance<85) {
        console.log("85%");
        return;
    };
    if (chance<95) {
        console.log("95%");
        return;
    };
    console.log("+95%");
}

function fightPirates() {
    var shipStrength = Math.ceil((food+gold)/2)+1;
    var pirateStrength = Math.ceil(Math.random()*shipStrength*2)+1;
    var goldToSteal = Math.round(pirateStrength/2)+1;
    if (pirateStrength>shipStrength) {
        var lostGold = 0;        
        if (gold>0) {
            if (goldToSteal>gold && gold >0) {
                lostGold = gold;
                gold = 0;
            } else {
                lostGold = goldToSteal;
                gold -= goldToSteal;
            }
            experience++;
            printMessage("Pirates!<br/>Carramba, we lost " + lostGold + " <img src='images/gold.png'> gold.");
        } else {
            if (Math.ceil(goldToSteal/2) > food && food > 0) {
                lostGold = food;
                food = 1;
            } else {
                lostGold = Math.ceil(goldToSteal/2);
                food -= lostGold;
            }
            experience++;
            printMessage("Pirates ahead!<br/>Carramba, we lost " + lostGold + " <img src='images/food.png'> food.");
        }
        
        
        //printStatus();
    } else {
        gold += goldToSteal; 
        experience+=2;
        printMessage("Pirates!<br/>We plunder " + goldToSteal + " <img src='images/gold.png'> gold.");
        //printStatus();
    }

};

//Enter Islands
function islandTrade() {
    var islandsFood = experience + gold;
    var foodCost = Math.ceil(Math.random() * islandsFood);
    if (gold > foodCost) {
        food+=islandsFood;
        gold-=foodCost;
        experience+=2;
        tradeMsg.innerHTML = "<span>We buy " + islandsFood + " </span><img src='images/food.png'><span> for " 
        + foodCost + " </span><img src='images/gold.png'><span>, captain!</span>";
    } else {
        experience++;
        tradeMsg.innerHTML = "Prices are high these days!<br/>No enough dinero, captain!<br/>Fight pirates we must<br/>or other days we come.";
    }
    //printStatus();    
    panel.style.display = "";
    window.removeEventListener("keydown", keydownHandler, false);
    sailToSeaBtn.addEventListener("click", returnToMap, false);
}

//Close trade panel
function returnToMap() {
    sailToSeaBtn.removeEventListener("click", returnToMap, false);
    panel.style.display = "none";
    window.addEventListener("keydown", keydownHandler, false);    
}

//Enter HQ, End Game
function endGame(msg) {
    printMessage(gameMessage);
    printStatus();
    //endTheGame.style.display = "";
    window.removeEventListener("keydown", keydownHandler, false);
}

//GUI funcions
function printMessage(gameMessage){    
    msg.innerHTML = gameMessage;    
}

function printStatus(){    
    stats.innerHTML = "<img src='images/gold.png'><span>" + gold + "</span>" 
    + "<img src='images/food.png'><span>" + food + "</span>" 
    + "<img src='images/score.png'><span>" + experience + "</span>" 
    + "<img src='images/days.png'><span>" + days + "</span>";
}


//initialize game
(renderMap)();

Grze