【问题标题】:Scrollable board for HTML5 GAME should use SVG's or CanvasHTML5 GAME 的可滚动板应使用 SVG 或 Canvas
【发布时间】:2018-11-16 05:19:43
【问题描述】:

我希望创建一个包含平铺板的简单网页游戏。我为每个正方形的背景收集了一组 svg(即一个用于草,一个用于石头,一个用于泥土等)。我还有 svg 用于显示在背景上方图层上的项目(例如树、木头、剑)。

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

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

只需要在最新版本的现代浏览器中工作

最好的方法是什么:

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

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

每次看板滚动添加新的 SVG,因为它们变得可见,删除 SVG 移动看板。将所有现有的 SVG 翻译适当的数量。

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

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

【问题讨论】:

    标签: javascript css svg canvas game-engine


    【解决方案1】:

    下面的示例使用两个模块 svg 来加载图像(任何图像格式都可以)和处理平移、缩放和渲染的板。它还提供了一个 onClick 事件,该事件将为您提供一个描述已单击的图块的对象。

    <!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>

    【讨论】:

    • 我在寻找一般建议,一个完整的例子是一个非常好的答案。这将使用方法1对吗?实际上,我前一段时间自己写了一个,但它闪烁,所以我想知道我是否使用了错误的方法。不过你的没有这个问题。
    • 我无法确定您为什么会遇到闪烁问题(可能是舍入问题或屏幕撕裂),但在这里我选择在推送新帧时使用 requestAnimationFrame。
    猜你喜欢
    • 2012-07-29
    • 2012-09-04
    • 2013-08-09
    • 2011-08-31
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 2011-08-18
    • 2016-07-11
    相关资源
    最近更新 更多