标签:
本文目的是分解前面的代码。其实,它得逻辑很清楚,只是对于我这种只是用过 Canvas 画线的人来说,这个还是很复杂的。我研究这个背景天空也是搞了一天,下面就是只加载天空的代码。
在线效果点击:http://1.codemo2.sinaapp.com/3d_demo_265line/index.html 【可以用键盘“左右”键控制】【手机浏览器触控有些异常】
原理大概就是:
1. 创建主循环 一个无限循环
2. 主循环内重复调用绘制方法
3. 绘制方法: 针对 Player 的位置和方向,绘制背景图
其中用到了 H5 的 requestAnimationFrame(callback),bind(this, argu,...) 比较难以理解的函数。
Player 是人物,含有平面x,y位置和方向三个特性;Controls 用来响应键盘和触屏操作;Map 是背景图还有后面的墙壁;Camera 是最重要的摄像机,用来绘制我们看到的炫酷图像;GameLoop 是整个程序的入口,一直循环调用 Camera 刷新绘制图形。
1 <!-- 2 3 1. draw sky 4 5 6 7 8 --> 9 10 <!doctype html> 11 <html> 12 <head> 13 <meta charset="utf-8"> 14 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> 15 <title>Raycaster Demo - PlayfulJS</title> 16 </head> 17 <body style=‘background: #000; margin: 0; padding: 0; width: 100%; height: 100%;‘> 18 <canvas id=‘display‘ width=‘1‘ height=‘1‘ style=‘width: 100%; height: 100%;‘ /> 19 20 <script> 21 22 var CIRCLE = Math.PI * 2; 23 var MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) 24 25 function Controls(){ 26 this.codes = { 37: ‘left‘, 39: ‘right‘, 38: ‘forward‘, 40: ‘backward‘ }; 27 this.states = { ‘left‘: false, ‘right‘: false, ‘forward‘: false, ‘backward‘: false }; 28 document.addEventListener(‘keydown‘, this.onKey.bind(this, true), false); 29 document.addEventListener(‘keyup‘, this.onKey.bind(this, false), false); 30 document.addEventListener(‘touchstart‘, this.onTouch.bind(this), false); 31 document.addEventListener(‘touchmove‘, this.onTouch.bind(this), false); 32 document.addEventListener(‘toucheend‘, this.onTouchEnd.bind(this), false); 33 } 34 35 Controls.prototype.onTouch = function(e){ 36 var t = e.touches[0]; 37 this.onTouchEnd(e); 38 if (t.pageY < window.innerHeight * 0.5) this.onKey(true, { keyCode: 38 }); 39 else if (t.pageX < window.innerWidth * 0.5) this.onKey(true, { keyCode: 37 }); 40 else if (t.pageY > window.innerWidth * 0.5) this.onKey(true, { keyCode: 39 }); 41 } 42 43 Controls.prototype.onTouchEnd = function(e){ 44 this.states = { ‘left‘: false, ‘right‘: false, ‘forward‘: false, ‘backward‘: false }; 45 e.preventDefault(); 46 e.stopPropagation(); 47 } 48 49 Controls.prototype.onKey = function(val,e){ 50 var state = this.codes[e.keyCode]; 51 if (typeof state === ‘undefined‘) return; 52 this.states[state] = val; 53 e.preventDefault && e.preventDefault(); 54 e.stopPropagation && e.stopPropagation(); 55 // console.log(e.keyCode); 56 } 57 58 function Bitmap(url, width, height){ 59 this.image = new Image(); 60 this.image.src = url; 61 this.width = width; 62 this.height = height; 63 } 64 function Map(){ 65 this.skybox = new Bitmap(‘assets/deathvalley_panorama.jpg‘, 2000, 750); 66 } 67 68 function Player(x, y, direction){ 69 this.x = x; 70 this.y = y; 71 this.direction = direction; 72 } 73 74 //弧度制 75 Player.prototype.rotate = function(angle){ 76 console.log(angle); 77 this.direction = (this.direction + angle + CIRCLE) % (CIRCLE); 78 } 79 80 Player.prototype.update = function(controls, map, seconds){ 81 if (controls.left) {this.rotate(-Math.PI * seconds)}; 82 if (controls.right) this.rotate(Math.PI * seconds); 83 // console.log("sdf"); 84 } 85 86 87 88 //http://www.ituring.com.cn/article/50019 89 //camera renderer scene 90 //resolution : 分辨率 91 function Camera(canvas, resolution , focalLength){ 92 this.ctx = canvas.getContext(‘2d‘); 93 this.width = canvas.width = window.innerWidth * 0.5; 94 this.height = canvas.height = window.innerHeight * 0.5; 95 this.resolution = resolution; 96 this.spacing = this.width / resolution; 97 this.focalLength = focalLength || 0.8; 98 this.range = MOBILE ? 8 : 14; 99 this.lightRange = 5; 100 this.scale = (this.width + this.height) / 1200; 101 } 102 103 Camera.prototype.render = function(player, map){ 104 this.drawSky(player.direction, map.skybox, map.light); 105 } 106 107 //ambient: environment light 108 Camera.prototype.drawSky = function(direction, sky, ambient){ 109 var width = sky.width * (this.height / sky.height) * 2; 110 var left = (direction / CIRCLE) * -width; 111 112 this.ctx.save(); 113 this.ctx.drawImage(sky.image, left, 0, width, this.height); 114 if (left < width - this.width) { 115 this.ctx.drawImage(sky.image, left + width, 0, width, this.height); 116 } 117 this.ctx.restore(); 118 } 119 120 function GameLoop(){ 121 // this.start = 122 this.lastTime = 0; //control FPS 123 this.frame = this.frame.bind(this); 124 this.callback = function(){}; //place holder 125 } 126 127 128 //requestAnimationFrame make borswer start animate,argu is callbadk 129 GameLoop.prototype.start = function(callback) { 130 this.callback = callback; 131 requestAnimationFrame(this.frame); 132 // body... 133 } 134 135 GameLoop.prototype.frame = function(time){ 136 var seconds = (time - this.lastTime) / 1000; 137 this.lastTime = time; 138 if (seconds < 0.2) this.callback(seconds); 139 requestAnimationFrame(this.frame); 140 } 141 142 143 var display = document.getElementById(‘display‘); 144 var player = new Player(15.3, -1.2, Math.PI * 0.3); 145 var camera = new Camera(display, MOBILE ? 160 : 320, 0.8); 146 var map = new Map(); 147 var controls = new Controls(); 148 var loop = new GameLoop(); 149 150 loop.start(function frame(seconds){ 151 //update map 152 // update player 153 player.update(controls.states, map, seconds); 154 // console.log("refresh.."); 155 camera.render(player, map); 156 }); 157 158 159 160 </script> 161 </body> 162 </html>
265行JavaScript代码的第一人称3D H5游戏Demo【个人总结1】
标签:
原文地址:http://www.cnblogs.com/muyun/p/5095366.html