HTML5 Canvas: 干净的JavaScript &代码组织允许更快速,更易于扩展

分享于 

17分钟阅读

Web开发

  繁體

介绍

在编写另一系列文章时( 从下面开始: 在计算过程中,我开始考虑一个简单的游戏/新奇的程序,它将动态显示和显示一个新的程序。 我还在阅读关于OOP的旧书,并考虑这些生物如何管理自己以及代码可能看起来像什么。

我调用RobotDots的final 程序是这个工作的结果。

动画机器人

活动示例

你可以在我的网站上看到 final 示例: http://raddev.us/RobotDots [ ^ ]

Background

尽管如此,我在原来的游戏中创建了我的小RobotDots游戏,但我意识到,我并不知道它的细节,这是免费的百科全书。 当然,我的is简单得多,并不像原来那么有趣。 基本上,对一组"生物生物"的概念的比较是随机生成的,具有一定的生命周期。

在RobotDots中,每个机器人有:
  • 出生( 生成并出现在屏幕上)
  • 一个年龄( 随着时间的推移增加)
  • 生命周期( 随机生成的值)
  • 颜色( 从集合中随机选择的值)
  • 透明度值( 随着年龄的增加变得更加透明)
  • 大小和 maxSize ( 每个对象将增长到它的单独的maxSize )
  • 位置( 最初随机生成的x,y 值计算移动)
  • 死亡( 当对象离开屏幕多次或者年龄增加到maxAge时)

这段代码有什么意义?

自管理对象

我没有试图创建一些大游戏或者任何一个很长时间来娱乐某人的东西。 相反,我对创建自管理对象感兴趣。 我想把大部分的代码都放在我称之为机器人的主要类型的encasulated中。

JavaScript OOP?

接下来,我感兴趣的是,如果我使用的是一个经典的面向对象语言(。C++,C#,Java ) 和 JavaScript ( Prototype面向对象),那么。

面向对象的&设计是否可以帮助?

我想回答的真正问题是: "即使在JavaScript中,以下OOP设计也能带来好处"?

剧透警报

有一个巨大的好处。 你的代码更易于管理。扩展。增强和修复。 考虑复杂的解决方案是令人惊讶的。

引用:

如果你可以采取复杂的详细信息并隔离它们,那么你可以使问题变得更容易。 这就是OOP的好处。

为什么要使用JavaScript和 HTML5?

我的background 是 C++,然后转换成 C#,所以我比其他语言更喜欢。 然而,我尝试使用正确的工具来工作。 在这种情况下,HTML5 Canvas 允许我做图形工作,并且当然可以使用现代浏览器。

你如何控制行动

  • 如果你点击棋盘上的任何地方,游戏将会暂停。
  • 如果按住Ctrl键并单击机器人,它将成为一个主机器人,并绘制所有相似颜色的线条。 ( 你也可以在电话和便笺上单击这里功能--的复选框)
  • 点击机器人会在周围创建一个 Highlight 环,这样你就可以跟踪机器人了。
  • 稍后我将添加点和其他效果。

用主机器人

现在让我们来检查一下。

应用程序启动:onload

注意 : 关于使用和后台网格绘图的更多细节,你可以阅读我在这里的其他系列的文章( 算法)的第一篇文章: 计算凸包&绘制 HTML5 Canvas ( 2的第 1部分) [ ^ ] )。

当浏览器事件的onLoad 触发时,它将调用我们的initApp() 方法,该方法只在应用程序启动时运行。 这让我可以在整个应用生命周期中找到一些我们需要的东西。 基本上,它设置网格 background 并初始化画布对象,我们需要完成所有绘图。

function initApp(){
 theCanvas = document.getElementById("gamescreen");
 ctx = theCanvas.getContext("2d");
 ctx.canvas.height = 650;
 ctx.canvas.width = ctx.canvas.height;
 theCanvas.addEventListener("mousedown", mouseDownHandler);
 intervalID = window.setInterval(mainGameLoop, 125);
 lineInterval = Math.floor(ctx.canvas.width/LINES);
 drawGameBoard();
}

SetInterval: 设置游戏循环

initApp() 还设置了一个定时器,每 125毫秒触发一次。 我们通过调用JavaScript方法 setInterval() 来设置它。 它非常容易使用,因为你只需提供方法 NAME ( 对方法--的引用没有括号) 和一个毫秒的时间间隔。 你可以看到,我已经命名了每次 mainGameLoop() 调用的方法。 这将成为整个应用的主要控制回路。

MainGameLoop

主游戏循环非常简单,因为基本上它只是遍历我们应用程序全局 array 中名为 allRobots[]的机器人对象的列表。 所执行的大多数代码都是隐藏 inside的每个机器人,因这里它更容易管理。

function mainGameLoop(){
 for (var idx = allRobots.length-1; idx> = 0;idx--){
 if (!allRobots[idx].isAlive){
 console.log("died at:" + allRobots[idx].age);
 if (allRobots[idx].isSelected){
 selectedCount--;
 }
 allRobots.splice(idx,1);
 if (masterRobot!= null){
 if (!masterRobot.isAlive){
 masterRobot = null;
 }
 }
 } 
 allRobots[idx].advanceAge();
 allRobots[idx].calculatePosition();
 }
 if (allRobots.length <48){
 allRobots.push(new robot({x:genRandomNumber(600),y:genRandomNumber(600),color:getRandomColor()}));
 }
 drawGameBoard();
 drawRobots();
 if (masterRobot!= null){
 drawConnectedRobots();
 }
}

游戏所做的全部总结都在游戏循环中

这里代码每 125毫秒触发一次,并执行以下操作:

  • 迭代整个机器人的array,并删除所有已经死亡的( isAlive == false )。
  • 确定是否有 MasterRobot ( 更多关于这个),如果有,则删除它。
  • 推进所有机器人的年龄( 游戏循环的每一个间隔是时间和机器人的年龄)
  • 计算每个roobt的新位置( 代表机器人在屏幕上的移动)
  • 如果 allRobots [] array 中有少于 48个机器人,那么我们添加一个新机器人( 一个是出生
  • 在游戏中绘制游戏板--表示我们必须在新位置绘制机器人,这意味着我们每次都需要重新绘制游戏板,太高。
  • 检查主机器人是否存在,如果它是drawConnecting线的机器人。

这就是OOP的威力。 复杂的工作在每个对象( 这个例子中的机器人对象) 中隐藏,这样我们就可以简单地总结整个程序的做法。

机器人类:这个应用的力量

机器人类是隐藏细节的地方,但是很容易创建一个新机器人。

如果你想创建一个,你必须做的就是:

var robot1 = new robot({});

奇怪语法

如果你没有做很多JavaScript编程,你可能会觉得很奇怪。 实际上,即使你有,它看起来也很奇怪。

简单的代码行通过发送一个空对象来构造一个新的机器人对象,该对象由 { }. 表示

如果正确设置对象,可以很容易地使用这个语法来初始化JavaScript中的对象。

让我们看一个较复杂的示例,这样我可以更清楚地解释这个概念。

创建模板类

在JavaScript中,通过创建一个函数创建一个模板类。 下面是一个示例:

function animal(initObj){
 this.name = initObj.name;
}

现在,如果你想要新建一个,你可以编写以下代码:

var cat = new animal({name:"cat"});

如果你想在控制台中打印动物的NAME,现在可以执行以下操作:

console.log(cat.name);

但是,如果开发人员试图创建一个没有发送对象的动物,那么他将得到一个错误声明: 无法读取未定义的属性'姓名'。

那是因为他把空的东西。

我们可以通过像下面这样发送一个空的( 非空对象) 来轻松修复这个问题:

var cat = new animal({});

但是,我们可以能需要一个默认值,因这里可以使用一些更奇怪的JavaScript来设置默认值。 让我们改变类,使它的看起来像下面这样:

function animal(initObj){ this.name = initObj.name || "mammal"; }

奇怪语法的解释

这种语法有些奇怪,但是它利用了一个事实:当传入对象是空的时,NAME 返回未定义的值( 它是JavaScript中的对象)。 当||与有效值相同时,右侧的有效值将为 true,NAME 属性将设置为该值。

现在,NAME 属性的默认值将为"哺乳动物"。

现在让我们看看机器人对象,你将在它的定义的顶部看到这种类型的初始化代码。

function robot (r){
 this.x = r.x || 200;
 this.y = r.y || 200;
 this.color = r.color || "black";
 this.size = r.size || 10;
 this.maxSize = r.maxSize || null;
 this.maxAge = r.maxAge || null;
 this.isSelected = r.isSelected || false;
 this.age = r.age || 1;
 this.isAlive = true;
 this.globalAlpha = r.globalAlpha || 1;
 this.offGridCount = 0;
 this.calculatePosition = function(){
 var flag = genRandomNumber(2);
 var addFlag = genRandomNumber(2);
 //console.log(flag);if (flag> 1){
 if (addFlag> 1){
 this.x += genRandomNumber(4) + genRandomNumber(3);
 }else{
 this.x -= genRandomNumber(4) + genRandomNumber(3);
 }
 }
 else{
 if (addFlag> 1){
 this.y += genRandomNumber(4) + genRandomNumber(3);
 }
 else{
 this.y -= genRandomNumber(4) + genRandomNumber(3);
 }
 }
 if (this.x> = 650 || this.x <= 0 || this.y> = 650 || this.y <=0)
 {
 this.offGridCount +=1;
 }
 if (this.offGridCount> =2){
 this.isAlive = false;
 }
 //console.log ("x :" + this.x +" y :" + this.y); }
 this.advanceAge = function() {
 // console.log("advanceAge...");if (this.age> = this.maxAge){
 this.isAlive = false;
 return;
 } 
 this.age +=1;
 // console.log("this.age :" + this.age);this.grow();
 }
 this.grow = function() {
 if (this.age % 100 == 0){
 if (this.maxSize == null || this.size <this.maxSize){
 this.size +=1;
 }
 }
 if (this.age % 200 == 0){
 this.globalAlpha -=. 1;
 if (this.globalAlpha <=. 2){
 this.isAlive = false;
 }
 }
 }
 this.drawRobotHighlight = function(){
 ctx.beginPath();
 ctx.lineWidth = 2;
 ctx.arc(this.x, this.y,this.size + 7,0,2*Math.PI);
 ctx.strokeStyle = "black";
 ctx.globalAlpha = 1;
 ctx.stroke();
 }
 this.drawRobot = function (){
 //console.log("robot size :" + allRobots[idx].size); ctx.fillStyle = this.color;
 ctx.strokeStyle= this.color;
 if (this.isSelected) {
 this.drawRobotHighlight();
 }
 ctx.globalAlpha = this.globalAlpha;
 ctx.beginPath();
 ctx.arc(this.x, this.y,this.size,0,2*Math.PI);
 ctx.stroke();
 ctx.fill();
 // reset opacity ctx.globalAlpha = 1;
 }
 this.initRobot = function(){
 this.maxSize = this.calcMaxSize();
 //console.log("maxSize :" + this.maxSize);this.calcMaxAge();
 }
 this.calcMaxSize = function(){
 return genRandomNumber(15) + 10;
 }
 this.calcMaxAge = function (){
 this.maxAge = genRandomNumber (40000) + 10000;
 //console.log ("maxAge :" + this.maxAge); }
 this.initRobot();
}

这就像很多代码,但是让我们来看看机器人实现的方法。

什么是机器人public 函数?

这是机器人能做的一切的List:

  • calculatePosition() - 生成用于模拟运动的X,Y 位置的值
  • advanceAge() - 添加到年龄值,使机器人变得更老
  • grow() - 添加到尺寸值以使机器人尺寸增大
  • drawRobotHighlight() - 绘制突出显示机器人的外部圆
  • drawRobot() - 在屏幕上绘制机器人以显示自身
  • initRobot() - 计算以后用于确定大小和当它死亡时使用的arraylist和 rtc
  • calcMaxSize() - 由initRobot调用以将maxSize初始化为随机值
  • calcMaxAge() - 由initRobot调用,将maxAge初始化为随机值。

这就是所有的代码。 每次 mainGameLoop() 触发所有细节 behind 时,这些方法都会很好地隐藏,这样你就可以理解它们的发生。

孤立地检查示例方法

好吧,即使有很多代码,我们现在可以看一个方法并确定它的作用。 让我们看看 drawRobotHighlight() 方法,我们将看到它是多么简单。

drawRobotHighlight ( )

这是在每个机器人被选中时绘制外部 black 环的代码。 这可能是非常困难的,因为机器人的大小都是不同的。 但是,由于这个代码被封装在每个单独的机器人上运行,所以我们非常简单。

this.drawRobotHighlight = function(){
 ctx.beginPath();
 ctx.lineWidth = 2;
 ctx.arc(this.x, this.y,this.size + 7,0,2*Math.PI);
 ctx.strokeStyle = "black";
 ctx.globalAlpha = 1;
 ctx.stroke();
 }

执行实际绘图的行是调用 ctx.arc() 方法的行。

你可以看到突出显示的机器人,它们在下面的图像中有圆圈:

高亮显示的项目

我们在它的x。y 位置绘制当前机器人对象( 前两个参数为 arc() 方法)。 但是我们也需要绘制它,所以它总是比它自己的半径大 7像素。 这很容易,因为你可以看到第三个参数是提供半径大小的地方。 这种情况下,只要在机器人半径大小为( 存储在它的大小属性中)的情况下添加 7即可。 现在,每次机器人生长和绘制时,Highlight 圆总是相对于特定机器人的尺寸。

我将让你检查其他方法,因为它们是自解释的( 连同我在这里引用的其他文章)。

但是,我想谈谈创建一个主机器人最有趣的特性。

创建主机器人

首先我没有想到允许用户点击任何机器人,并且让它的他机器人的相似颜色画线。 在我写完所有的代码之后,我就想到了它。 我想,"嘿,如果我有所有机器人的Collection,我知道他们的中心点,我可以轻松地画一条线。"

聚焦的力量

我能够想到这一点,并且关注这个功能,从而将每个机器人从屏幕的位置绘制出来。 现在,它只是在迭代点和绘图线的同一颜色值中的一个练习。 这是密码,这将是有意义的,效果是( 我觉得) 非常酷。

function drawConnectedRobots(){
 for (var i = 0; i <allRobots.length;i++){
 masterRobot.isSelected = true;
 if (masterRobot.color == allRobots[i].color){
 drawLine(masterRobot, allRobots[i], masterRobot.color); 
 }
 }
}

所有在它的位置绘制机器人的代码相隔离是非常简单的。

我希望这篇文章能让你考虑一下可以能使用的OOP的一个可以能的额外使用,以简化。

历史记录

首次发布:05-03-2016


JAVA  Javascript  ext  fast  Html5  CAN  
相关文章