HTML5 GAME的可滚动板应使用SVG或Canvas [英] Scrollable board for HTML5 GAME should use SVG's or Canvas

查看:97
本文介绍了HTML5 GAME的可滚动板应使用SVG或Canvas的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望创建一个简单的网页游戏,其中涉及一块木板.我为每个方形的背景收集了svg(即一个用于草,一个用于石材,一个用于污垢等).对于在背景上方的图层上显示的项目(例如树木,木头,剑),我也有svg.

I wish to create a simple webgame that involves a tiled board. I have a collection of svg's for the background of each square (i.e one for grass, one for stone, one for dirt etc). I also have svg's for items that will be displayed on the layer above the background (such as trees, wood, sword).

我有一个内存数据库,其中包含每个方格的背景以及是否包含以及包含哪个项目.

I have an in memory database of what the background for each square is and if and which item it contains.

我希望能够:
*放大或缩小
*向左或向右滚动
*向上或向下滚动
*在该正方形的背景上方显示项目

I wish to be able to:
* Zoom in or out
* Scroll left or right
* Scolll up or down
* Have items displayed above the background for that square

仅需要在最新版本的现代浏览器中运行

Only needs to work in recent versions of modern browsers

什么是最好的方法?

What is the best approach for this:

1.有一个画布对象.获取当前的缩放比例,最顶部的XY,画布宽度和画布高度.循环访问内存数据库中的方块,并在正确的位置打印相应的SVG.每次滚动或缩放时,都重新打印整个电路板.

1. Have a canvas object. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and print the corresponding SVG's in the correct locations. Each time it is scrolled or zoomed reprint the entire board.

2.有一个div.获取当前的缩放比例,最顶部的XY,画布宽度和画布高度.遍历内存数据库中的方块,并在正确的位置创建SVG.

2. Have a div. Get the current zoom, top most XY, canvas width and canvas height. Loop though the squares in the in memory database and create SVG's in the correct locations.

每次板滚动显示新的SVG时,请在板移动时删除SVG.适当翻译所有现有的SVG.

Each time the board scrolls add new SVGs as they become visible, delete SVGs as they move of the board. Translate all the existing SVGs by the appropriate amount.

每次板缩放时,都会根据新的缩放级别放大或缩小所有现有的SVG.

Each time the board zooms enlarge or shrink all the existing SVGs based on the new zoom level.

3.我不知道的第三种方法.

3. Some third approach that I am unaware of.

推荐答案

以下示例使用svg两个模块加载图像(任何图像格式均适用)&用于平移,缩放和放大的板渲染.它还提供了onClick事件,该事件将为您提供一个对象,该对象描述已被单击的图块.

The example below uses two modules svg to load images (Any image format will work) & board which handles panning, zooming & rendering. It also provides an onClick event which will give you an object that describes the tile that has been clicked on.

<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<style>
			body {
				background-color: black;
			}
			
			canvas {
				display: block;
				margin: 30px auto 0px auto;
				border: solid 1px white;
				border-radius: 10px;
			}
		</style>
	</head>
	<body>
		<canvas id="canvas"></canvas>
		<script type="application/javascript">
		
		var svg = function() {
		
			"use strict";
			
			var svgImages = [];
			var areImagesLoaded = true;
			var isPageLoaded = false;
			
			addEventListener("load",function() { isPageLoaded = true; isFinished(); });
			
			var exports = {
				onload: function() {},
				request: null,
				get: null
			};
			
			function isFinished() {
				if (isPageLoaded && areImagesLoaded) {
					exports.onload();
				}
			}
			
			function onSvgError() {
				this.isDone = true;
				console.warn("SVG " + this.src + " failed to load.");
			}
			
			function onSvgLoaded() {
				this.isDone = true;
				
				for (var id in svgImages) {
					if (!svgImages[id].isDone) {
						return;
					}
				}
				
				areImagesLoaded = true;
				isFinished();
			}
			
			function request(id,path) {
				if (svgImages[id]) {
					return;
				}
				
				areImagesLoaded = false;
				
				var img = document.createElement("img");
				
				img.onerror = onSvgError;
				img.onload = onSvgLoaded;
				img.isDone = false;
				img.id = id;
				img.src = path;
				
				svgImages[id] = img;
			}
			
			function get(id) {
				return svgImages[id];
			}
		
			exports.request = request;
			exports.get = get;
			
			return exports;
		
		}();
		
		var board = function() {
		
			"use strict";
			
			var canvasWidth = 0;
			var canvasHeight = 0;
			var canvas = null;
			var ctx = null;
			var frameRequested = false;
			
			var tileWidth = 0;
			var tileHeight = 0;
			var tileTypes = [];
			
			var boardWidth = 0;
			var boardHeight = 0;
			var board = [];
			var hasInitialized = false;
			
			var camera = {
				x: 0.0,
				y: 0.0,
				zoom: 1.0
			};
			
			function mapToBoard(x,y) {
				var invZoom = 1.0 / camera.zoom;
				
				return [
					(x - (canvasWidth >> 1)) * invZoom - camera.x,
					(y - (canvasHeight >> 1)) * invZoom - camera.y
				];
			}
			
			var isMouseDragging = false;
			var mouseStartX = 0;
			var mouseStartY = 0;
			var mouseLastX = 0;
			var mouseLastY = 0;
			
			var tileEvent = {
				background: "",
				foreground: "",
				x: 0,
				y: 0
			};
			
			function onTileSelected(e) {
				
			}
			
			function onMouseDown(e) {
				isMouseDragging = true;
			
				var bounds = canvas.getBoundingClientRect();
				
				mouseStartX = mouseLastX = e.clientX - bounds.left;
				mouseStartY = mouseLastY = e.clientY - bounds.top;
			}
			
			function onMouseUp(e) {
				isMouseDragging = false;
				
				var bounds = canvas.getBoundingClientRect()
				var x = e.clientX - bounds.left - mouseStartX;
				var y = e.clientY - bounds.top - mouseStartY;
				var l = Math.sqrt(x * x + y * y);
				
				if (l < 2.0) {
					[x,y] = mapToBoard(e.clientX - bounds.left,e.clientY - bounds.top);
					
					if (x > 0 && y > 0 && x < boardWidth * tileWidth && y < boardHeight * tileHeight) {
						x = (x / tileWidth) | 0;
						y = (y / tileHeight) | 0;
						
						var tile = board[x + y * boardWidth];
						
						tileEvent.background = tile.background;
						tileEvent.foreground = tile.foreground;
						tileEvent.x = x;
						tileEvent.y = y;
					} else {
						tileEvent.background = "";
						tileEvent.foreground = "";
						tileEvent.x = -1;
						tileEvent.y = -1;
					}
				
					onTileSelected(tileEvent);
				}
			}
			
			function onMouseMove(e) {
				if (hasInitialized && isMouseDragging) {
					var bounds = canvas.getBoundingClientRect();
					var x = e.clientX - bounds.left;
					var y = e.clientY - bounds.top;
					
					camera.x += (x - mouseLastX) / camera.zoom;
					camera.y += (y - mouseLastY) / camera.zoom;
					
					mouseLastX = x;
					mouseLastY = y;
					
					requestDraw();
				}
			}
			
			function onWheel(e) {
				if (Math.sign(e.deltaY) === -1) {
					camera.zoom *= 1.1;
				} else {
					camera.zoom *= 0.9;
				}
				
				requestDraw();
			}
			
			function draw() {
				ctx.fillStyle = "gray";
				ctx.fillRect(-canvasWidth >> 1,-canvasHeight >> 1,canvasWidth,canvasHeight);
				
				var _tileWidth = tileWidth * camera.zoom;
				var _tileHeight = tileHeight * camera.zoom;
				var _ox = camera.x * camera.zoom;
				var _oy = camera.y * camera.zoom;
				var _x = _ox;
				var _y = _oy;
				
				for (var x = 0; x <boardWidth; ++x) {
					for (var y = 0; y < boardHeight; ++y) {
						var index = x + y * boardWidth;
						var tile = board[index];
						var background = tileTypes[tile.background];
						var foreground = tileTypes[tile.foreground];
						
						if (background) {
							ctx.drawImage(
								background,
								_x,
								_y,
								_tileWidth,
								_tileHeight
							);
						}
						
						if (foreground) {
							ctx.drawImage(
								foreground,
								_x,
								_y,
								_tileWidth,
								_tileHeight
							);
						}
						
						_y += _tileHeight;
					}
					
					_y = _oy;
					_x += _tileWidth;
				}
				
				frameRequested = false;
			}
			
			function requestDraw() {
				if (!frameRequested) {
					frameRequested = true;
					requestAnimationFrame(draw);
				}
			}
			
			return {
				BACKGROUND: 0,
				FOREGROUND: 1,
			
				set canvas(canvasID) {
					if (!hasInitialized) {
						canvas = document.getElementById(canvasID);
						canvas.onmousedown = onMouseDown;
						canvas.onmouseup = onMouseUp;
						canvas.onmousemove = onMouseMove;
						canvas.onwheel = onWheel;
						ctx = canvas.getContext("2d");
					}
				},
				
				set canvasWidth(w) {
					if (!hasInitialized && canvas) {
						canvasWidth = canvas.width = w;
					}
				},
				
				set canvasHeight(h) {
					if (!hasInitialized && canvas) {
						canvasHeight = canvas.height = h;
					}
				},
				
				set tileWidth(w) {
					if (!hasInitialized) {
						tileWidth = w;
					}
				},
				
				set tileHeight(h) {
					if (!hasInitialized) {
						tileHeight = h;
					}
				},
				
				set width(w) {
					if (!hasInitialized) {
						boardWidth = w;
					}
				},
				
				set height(h) {
					if (!hasInitialized) {
						boardHeight = h;
					}
				},
				
				set onTileSelected(callback) {
					onTileSelected = callback;
				},
				
				get width() {
					return boardWidth;
				},
				
				get height() {
					return boardHeight;
				},
				
				get onTileSelected() {
					return onTileSelected;
				},
				
				defineTileTypes: function(types) {
					if (types.length % 2 !== 0) {
						return;
					}
				
					for (var i = 0; i < types.length; i += 2) {
						var id = types[i];
						var img = types[i + 1];
						
						tileTypes[id] = img;
					}
				},
				
				init: function() {
					camera.x = -(boardWidth >> 1) * tileWidth;
					camera.y = -(boardHeight >> 1) * tileHeight;
					ctx.translate(canvasWidth >> 1,canvasHeight >> 1);
				
					board.length = boardWidth * boardHeight;
					
					for (var i = 0; i < board.length; ++i) {
						board[i] = {
							background: "",
							foreground: ""
						};
					}
					
					hasInitialized = true;
					requestAnimationFrame(draw);
				},
				
				set: function(type,id,x,y) {
					if (hasInitialized
					&& tileTypes[id]
					&& x > -1 
					&& x < boardWidth
					&& y > -1
					&& y < boardHeight) {
						var index = x + y * boardWidth;
						
						if (type === this.BACKGROUND) {
							board[index].background = id;
						} else {
							board[index].foreground = id;
						}
						
						requestDraw();
					}
				}
			};
		
		}();
		
		void function() {
		
			"use strict";
			
			svg.request("grass","https://i.stack.imgur.com/CkvU7.png");
			svg.request("water","https://i.stack.imgur.com/an6a5.png");
			
			svg.onload = function() {
				board.canvas = "canvas";
				board.canvasWidth = 180;
				board.canvasHeight = 160;
				
				board.tileWidth = 25;
				board.tileHeight = 25;
				
				board.width = 20;
				board.height = 20;
				
				board.defineTileTypes([
					"GRASS",svg.get("grass"),
					"WATER",svg.get("water")
				]);
				
				board.init();
				
				for (var x = 0; x < board.width; ++x) {
					for (var y = 0; y < board.height; ++y) {
						board.set(board.BACKGROUND,"WATER",x,y);
						
						if (Math.random() > 0.2) {
							board.set(board.BACKGROUND,"GRASS",x,y);
						} else {
							board.set(board.BACKGROUND,"WATER",x,y);
						}
					}
				}
			}
			
			board.onTileSelected = function(e) {
				if (e.background === "GRASS") {
           board.set(board.BACKGROUND,"WATER",e.x,e.y);
        } else {
           board.set(board.BACKGROUND,"GRASS",e.x,e.y);
        }
			}
			
		}();
		
		</script>
	</body>
</html>

这篇关于HTML5 GAME的可滚动板应使用SVG或Canvas的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆