3

点赞

1

回复

0

浏览

如何编写你的坦克

坦克游戏简介 访问 https://tank.tianmaying.com https://tank.tianmaying.com ,进入天码营的坦克对战游戏平台。 左侧可以编写你的坦克代码,右侧则是对战面板;编写好代码后,点击开始就能调试你的坦克,在右侧面板看坦克的行为。 同时你可以点击坦克库,从坦克库中选择一个或者多个坦克进行对战。 面板上有多个坦克的情况下,点击开始即能进行对战。对战开始时,可以进行暂停/继续和停止等操作。 编写坦克的基本思路 坦克初始有5条命,如果坦克被子弹击中5次,坦克就挂掉啦 坦克的外形和名字可以进行配置,这会让你的坦克更个性化,但是不会影响战斗能力 坦克以某个方向前进,以该方向为轴,轴的左侧和右侧90度(即一共180度的视野)的夹角内出现敌人,会收到通知,你可以进行响应 坦克击中敌人或者被敌人击中,你可以进行响应 进行响应的方式,即实现回调方法(详见后续内容),进行转向、前进和射击等操作 编写坦克就是典型的面向对象编程的方法,通过设置对象的属性和实现对象的函数来编写一辆有战斗力的坦克。 如果你想了解游戏实现的更多细节,请参考最后一节游戏引擎实现 创建坦克 使用下面的代码,你就创建了一辆静止的坦克: new Tank();...

javascript

3

点赞

0

回复

0

浏览

天码营坦克游戏API

EVENTS Events Description onStart() Game begins onBulletHit(enemyName, enemyPosition) Enemy hit onHitByBullet(enemyName, enemyPosition) Hit by enemy onHitWall() Hit wall onEnemySpotted(enemyPosition) Enemy spottedATTRIBUTES Attributes Description name Name of your tank shape Image url of your tank color When you use the default shape, you can chose color from blue(default), green, purple, yellow, redMETHODS Methods Description ahead(distance, onFinish) Go ahead by distance steps fire() Fire in the current direction getBattleFieldSize() Get the size of battle field getDirection() Get the current direction of your tank getPosition() Get the current position of your tank getSize() Get the size of your tank getLife() Get life of your tank say(message, ...) Send a broadcast message to all players turn(deg, onFinish) Turn deg degrees clockwise turnLeft(onFinish) Turn left by 90° turnRight(onFinish) Turn right by 90°API REFERENCE ONBULLETHIT(ENEMYNAME, ENEMYPOSITION) enemyName : String , eg. tank enemyPosition x : Number , eg. 200 y : Number , eg. 200 direction : Number , eg. 200 ONHITBYBULLET(ENEMYNAME, ENEMYPOSITION) enemyName : String , eg. tank enemyPosition x : Number , eg. 200 y : Number , eg. 200 direction : Number , eg. 200 ONENEMYSPOTTED(ENEMYPOSITION) enemyPosition x : Number , eg. 200 y : Number , eg. 200 direction : Number , eg. 200 AHEAD(DISTANCE, ONFINISH) distance : Number , eg. 200 onFinish : Function , eg. function(){this.turn(90)} Returns: undefined FIRE() Returns: undefined GETBATTLEFIELDSIZE() Returns: Object , eg: {width: 32, height: 43} GETDIRECTION() Returns: Number in degrees, eg. 90 GETPOS() Returns: Object , eg. {left: 32, top: 300} GETSIZE() Returns: Object , eg. {width: 43, height: 43} GETLIFE() Returns: Number , eg. 4 SAY(MESSAGE, ...) message : String , the message you want to send, arbitrary message arguments are allowed. Returns: undefined TURN(DEG, ONFINISH) deg: Number , clockwise if positive, anticlockwise if negative. onFinish: Function , eg. function(){this.ahead(200)} Returns: undefined TURNLEFT(ONFINISH) onFinish: Function , eg. function(){this.ahead(200)} Returns: undefined TURNRIGHT(ONFINISH) onFinish: Function , eg. function(){this.ahead(200)} Returns: undefined ...

javascript

0

点赞

0

回复

701

浏览

JavaScript框架jQuery入门

JQUERY入门 jQuery http://jquery.com/ 是一个快速简单的JavaScript工具库,为快速开发提供了DOM遍历、事件处理、动画、AJAX交互几个方面的工具。jQuery会改变你写JavaScript的方式。 先来看看jQuery用起来是怎样的: // 给拥有continue类的 元素设置文本为:Next Step......

jqueryjavascript

13

点赞

8

回复

6629

浏览

使用天码营前端预览工具:Web前端开发(HTML/CSS/JavaScript)实验

本实验将创建一个简单的HTML页面,展现北京大学创新工程实践课程的老师的信息。 更酷的是,我们可以通过向微信公众号发送消息,来为这个HTML页面发送弹幕。 大家做完实验,向这个公众号发送弹幕,即可在页面上看到自己发送的信息以弹幕的形式在HTML页面上飞出来。 同学们将在这个实验中学习HTML、CSS和JavaScript的基本使用,对于Web前端开发有一个初步了解。 最终的效果图如下: 实验要求 1. 团队页面: 请同学们参考接下来的实验步骤,实现一个展示团队成员信息的HTML页面。通过点击分享生成链接,将链接发送给老师。分享时请输入学校和团队名称。实验步骤是展示创新工程实践的老师信息,你们需要替换成自己团队成员的信息。当然你们可以添加更多酷炫的效果,比如这是吉林大学的同学们的一个很酷的 作品 https://www.tianmaying.com/snippet/318/external preview 。 2. 个人页面: 请每一个团队成员实现一个展示个人信息的HTML页面,个人页面如何制作请参考另外一篇文章, 使用HTML5打造一个漂亮的个人简介页面 https://www.tianmaying.com/tutorial/morden profile 。在团队页面中,每个人的名字请增加链接,链接到个人介绍页面。个人介绍页面的链接地址可以通过分享按钮生成。 实验概览 实验分为三个步骤: 1. 编写HTML代码,展示我们希望显示的内容 2. 编写CSS代码,为内容增加样式,让展示更加美观 3. 编写JavaScript代码,让页面出现动态的弹幕效果 整个实验通过 前端预览工具 http://preview.tianmaying.com/ 完成,请访问 https://preview.tianmaying.com https://preview.tianmaying.com/ 前端预览工具的截图如图所示: 预览工具一共分为四个部分: 左上角为HTML代码输入区域 右上角为CSS代码输入区域 左下角为JavaScript代码输入区域 右下角的效果预览区域,输入代码后可以实时看到效果变化 可以点击右上角的 分享 ,生成这个页面的URL链接,在微信和微博中分享。 1 编写HTML代码 1.1 创建HTML页面框架 首先创建一个HTML页面的框架,HTML页面由 和 两部分构成。 与 之间可以编写基本的元信息,比如字符编码 与 之间是HTML的正文部分 注意大部分标签都是成对出现的,后面出现的标签会有一个 / 符号。 打开前端预览工具,已经默认给大家创建好了一个HTML页面框架: ...

javascriptcsshtml

1

点赞

0

回复

1021

浏览

JavaScript编程入门简介:DOM、AJAX与Chrome调试器

JavaScript最广泛的用途仍然是浏览器端脚本。本文便来细述在浏览器中如何引入JavaScript,以及Chrome浏览器的调试功能。 我们知道 Javascript 等于 ECMAScript + 宿主环境。宿主环境提供了一系列的全局对象,例如Node.js提供的 require , console 等;浏览器提供的DOM、BOM对象。本节课我们便来了解浏览器,熟悉客户端脚本开发和调试。 HTML中引入JS Javascript 已经成为 Web 页面逻辑的默认脚本语言,可以直接在 HTML 文件中引入而不需表明脚本语言。Javascript 使用 标签引入,该标签可以出现在 中,也可以出现在 中。 在HTML4中, 标签需要指定脚本类型: type="text/javascript" 。现在已经不必这样做了。JavaScript 是 HTML5 默认的脚本语言。 在HTML中引入Javascript有两种方式:内嵌JavaScript和外部JavaScript文件。 内嵌JAVASCRIPT 内嵌JavaScript(Inline JavaScript) 是指在HTML中添加 标签,并将JavaScript代码直接写在标签的内容中。 ...

javascript

0

点赞

0

回复

599

浏览

10分钟了解Javascript

Javascript http://en.wikipedia.org/wiki/JavaScript 是动态的,弱类型的,解释执行的程序设计语言。 Javascript极其灵活,支持多种程序设计范式:面向对象、指令式、函数式。JavaSCript最初被用于浏览器脚本,现在已经是所有主流浏览器的默认脚本语言。浏览器脚本的作用包括用户交互、DOM操作、以及与服务器通信。 Node.js https://nodejs.org/en/ 的流行已经将JavaScript从浏览器端引入到服务器端,并显示出卓越的并发性能。 Javascript 由 Netscape 浏览器首次引入到Web文档中。Javascript的出现使web文档变得更像动态的App,而不是静态的文档。现在主流的浏览器都有javascript支持。Javascript核心被标准化为 ECMAScript http://en.wikipedia.org/wiki/ECMAScript 。 JavaScript作为一门通用编程语言(general purpose),其语法、内置对象、以及工具库需要大量的精力才能掌握,但JavaScript有着直观的语法,是一门非常容易上手的编程语言。本章中介绍JavaScript最基本的语法,然后编写一个简单的Javascript程序。 HELLO WORLD 使用你喜欢的文本编辑器,建立一个javascript文件(后缀命名为 .js 即可),输入以下的内容: console.log('Hello, world!')...

javascript

0

点赞

0

回复

620

浏览

函数式JavaScript编程

也许你还觉得函数式编程很陌生,但许多的函数式编程风格已经渐渐开始流行。 比如 DOM https://en.wikipedia.org/wiki/Document_Object_Model 中常见的回调函数、 jQuery http://jquery.com/ 的级联写法、 lodash https://lodash.com/ 的partial函数、Java的 Guava http://code.google.com/p/guava libraries 库... 到处都是函数式的编程风格。 啥叫函数式编程?Lisp?事实上只要语言将函数作为一等公民(或者借助工具达到类似效果) 就可以支持函数式编程。而将函数作为一等公民意味着函数可以像变量一样传参、赋值和返回。 回调函数 这正是函数作为一等公民的一大特征:函数可以作为参数传递。前端人员可能对回调函数非常熟悉,回调在处理不连续的事件时特别方便。例如从浏览器获取服务器的数据并更新页面时,最直接的实现可能是这样: var request = make_request();...

javascript

0

点赞

0

回复

553

浏览

JavaScript 对象有哪些用法?

JavaScript http://www.tianmaying.com/tutorials/tag/javascript 是面向对象语言,通过原型机制实现继承,通过『闭包』等方式可以实现封装。 本文来探讨JavaScript对象的特殊之处:原型链、引用、反射、属性遍历等特性。 对象创建 JavaScript http://www.tianmaying.com/tutorials/tag/javascript 拥有非常直观的对象创建方式: var emptyObject = {};...

javascript

0

点赞

0

回复

577

浏览

那些 JavaScript 的优秀特性

说起编程语言,理科生们最初接触的应当是C++。那是P校的必修课。 事实上第一个让我认真去用的语言是.NET C , 我学习了几乎所有的语言特性、窗口控件以及WPF样式字典。 这样便可以成为一个C 伪专家,去解答别人关于C 的各种问题 :) 此后由于各种原因去学习JavaScript、Python、Ruby,逐渐地发现成功使用一门语言的关键不在于熟悉所有语言特性,而是学着只用那些优雅的、表达力强的语言特性来完成整个工程。 那么 JavaScript http://tianmaying.com/tutorials/javascript 能否进化为一个只包含有点的语言呢? 在这一点上标准化协会其实也无能为力,去除任何语言特性都会造成依赖于该特性的工程失效。 我们可以使用JSLint等工具来提示我们,确保项目中没有使用那些糟糕的语言特性。 JAVASCRIPT 的诟病 JavaScript可以是天使,也可以是魔鬼! 多数语言都有自己的优势和缺憾,JavaScript也不例外。 我们能做的就是尽量去使用那些优雅的部分,而避免使用那些复杂的、易出错的特性。 JavaScript的成长过程并不完美。我们知道Java语言的设计经过了完整的标准化和设计流程, 堪称是完美的面向对象编程语言。 而JavaScript被Netscape浏览器引入之后便迅速地被全球开发者采用, 甚至都没有实验室的测试期。虽然JavaScript没有完美的成长过程, 但它仍然包含了非常多优雅的、表达力强的语言特性,这也是它能打败Java Applet的原因之一。 JavaScript并不是非常难用的语言。浏览器成就了JavaScript的流行, 而DOM的糟糕也使得JavaScript广受诟病。 DOM甚至还没有完整的标准,不同浏览器实现也不一致, 这使得任何语言都很难操作DOM,这并非JavaScript的问题。 与此相反,JavaScript的神奇之处在于任何人都可以马上上手, 不需要了解多少JavaScript语言特性,甚至不需要会编程。 JAVASCRIPT 的两面 JavaScript的设计中包含了优秀的部分和糟糕的部分。 其中优秀的特性包括:函数、弱类型、动态对象、优雅的对象表示法; 全局变量则是最糟糕的特性之一。 函数是JavaScript的一级对象,可以被传参、返回以及赋值。 同时JavaScript函数也是特殊的,它拥有词法作用域,这也是闭包的原理。 弱类型。当今编程语言都比较偏向强类型,其设计思想是将错误的发现提前到编译期。 尤其是C++:如果编译通过,那么程序就应当正确运行,否则就是代码设计有问题。 但任何强类型语言都少不了测试,那么为何不使用更轻松的弱类型语言呢? 况且强类型语言给出的编译错误大多不是代码中真正的问题所在。 Java给出的数百行调用堆栈纯属无用,除非程序员是一台电脑。 JavaScript对象。JavaScript的对象表示法启发了JSON数据格式, 列举属性的方式确实更加直观,更加具有表达力。 JavaScript的原型继承方式却是一个备受争议的特性。 全局变量应当是最糟糕的设计。JavaScript需要全局变量来完成链接。JavaScript链接时会将所有编译单元的全局变量合并到 global 对象下。 JAVASCRIPT代码片段 下面给出一段JavaScript来感受一下JavaScript中的优雅。 这是 brick.js https://github.com/harttle/brick.js 项目中的JavaScript代码片段,它们的用途分别是更新 this.modules 以及合并编译LESS代码。 Static.prototype.update = function(type) {...

javascript

0

点赞

0

回复

761

浏览

jQuery社会化分享:支持微信、微博、Github

一个既美观有支持微信的社会化分享工具: harttle/social share https://github.com/harttle/social share 。 支持微信(二维码)、微博、Github、Google+、LinkedIn、Twitter、Facebook、RSS..., 支持四种大小设置。 在线Demo: http://harttle.com/social share/ http://harttle.com/social share/ 文档: https://github.com/harttle/social share https://github.com/harttle/social share 最近在 天码营 http://tianmaying.com/ 和本博客中添加社会化分享,调研和尝试了不少第三方工具。 国内最方便的应当是 jiathis http://www.jiathis.com/ ,然而并不美观。国外的工具基本都没有微信(二维码)的支持。 于是诞生了 harttle/social share https://github.com/harttle/social share 。 效果图 第三方依赖 harttle/social share https://github.com/harttle/social share 依赖于 Fontawesome http://fontawesome.io/ 和 jQuery http://jquery.com/ , 需要先引入这两个第三方库: ...

javascriptjquery

0

点赞

0

回复

937

浏览

敏捷课程总结报告-shakespeare文字游戏引擎组

一、应用介绍 shakespeare是一款文字游戏引擎,面向人群是有剧本制作,游戏开发情结的人们。无需任何编程基础,通过markdown制作故事剧本即可一键生成可以在微信公众号,pc端浏览器和手机浏览器页面上运行的小游戏。 二、系统架构 下图是我们的系统框架图。 有游戏作者,文字游戏云和用户三方。 首先游戏作者本地用markdown语法编写游戏剧本,Engine模块处理后生成json数据包上传至游戏库。服务器应答来自微信公众号或者浏览器网页的请求,进行和游戏玩家的交互。 各部分功能 ENGINE Engine端负责将作者写的DSL游戏脚本转化为Json格式的游戏,并发送post请求到服务端容器完成建立游戏的工作。 它的处理流程是: 用户提交游戏 检查用户语法 检查用户的游戏是否有错误 检查无误后进行提交 发送POST请求道服务端容器 JSON游戏库 虽然叫做Json游戏库,但实际游戏是在关系型数据库中存储的,每个游戏都是一个有向图,通过 Point , Line , Selection , Game 等元素描述。 当服务端接收到 Engine 发送的游戏Json数据时,会首先将之转化为上述对象,检查无误后,格式化存储在Mysql中。当微信用户或浏览器用户发送请求到服务端时,服务端会从数据库中拿到游戏数据,并将之做相应的处理,返回相应的数据。 微信自动机 微信自动机负责完成微信公共号用户的游戏逻辑,用户发送命令给微信自动机,自动机会向游戏库中读取游戏数据还原游戏有向图,根据用户在图中的位置,及他以前及刚刚的选项判断下一步该去的节点,并将节点的内容及关联的选项返回给用户。 微信自动机除了负责自动机的相关逻辑外,它还要和腾讯的微信公共平台打交道,当用户向公共号发送一个消息时,消息会首先发送给腾讯的微信公共平台,腾讯会将之编码为XML格式转发给我们的微信自动机,微信自动机会解析XML,并转化为用户命令,完成逻辑后得到返回文字,并编码为XML格式返回给微信公共平台,完成整个通信过程。 网页版 网页版满足了浏览器用户的游戏需求,当用户访问网页时,浏览器端会向服务器端发送AJAX请求获取游戏数据,渲染出游戏内容,供用户进行游戏。 服务端 在下一部分我们将详细介绍上述各个模块的技术原理。 三、模块介绍 游戏引擎模块 负责人:何远舵 需求设想 我负责这部分的功能主要包括 接受用户输入 , 语法解析 , 语义解析 , 向后台通信 , 向用户反馈 。 方案尝试 上述 需求设想 需求设想 比较笼统,而涉及到具体的技术选择、制品的形态等就更是未知数了,因为不存在一个面向这类问题的通用框架或解决方案。因此在技术选择以及具体实现上,调研并尝试了许多可能的方案,所以下面介绍一下曾经尝试过的方案及其各自的优劣。 JAVA + ANTLR4 最开始设想从语言角度出发,因此首先调研了一些语法解析器生成器工具。一个比较好的选择是 Antlr4(ANother Tool for Language Recognition) 。 它是一个功能强大的解析器生成,可以实现读取、处理、执行、转义文本文件或二进制文件的功能。因此,它被广泛用于定制领域语言。很自然地,我也选择了用它去构建DSL。此外,虽然它支持输出多种语言,但是考虑到语言的统一,故全部采用了Java开发。 既然已经选择了Java和Antlr4,那么一个比较直接的产品形态就是作为后台的服务进程实现。因此,该产品应该是这样工作的,用户通过一个通道(HTTP Post)提交文本,该进程收到并进行语法语义检查,反馈用户成功或错误,如果成功则再把解析好的中间结果发送到其他服务用于生成游戏等。 但是该方案有如下 缺点 : 1. 开发上 :虽然使用了构建工具,但是使用Java以TDD的方式开发(先写单元测试),开发进度缓慢,也许是因为我不用IDE的缘故; 2. 开销 :作为后台服务存在,会占用后台的计算和IO资源; 3. 用户体验 :用户交互过程太过丑陋,或者说完全没考虑。 考虑到上述的缺点,便把这一版直接都丢了,残余代码在onboard的 engine 分支下。 RUBY + TREETOP + GRAPE 为了改进上述缺点,我尝试了Ruby、Treetop、Grape的技术架构。Ruby我更为熟悉,Treetop是ruby版本的dsl语言定制工具,而Grape则是一个Ruby的REST like API微框架。 很明显,这一套方案的选择侧重点在两个方面:一方面选择熟悉的技术语言Ruby作为开发语言有利于 提高生产效率 ,另一方面,相比较前一个解决方案完全没有考虑用户反馈的做法,该方案选择Grape则是开始把工作重心往 用户体验 方面挪,希望通过后台解析后得到结果的json,返回给前端,并在前端展示出成功与否或问题所在。当然这套方案依然将解析工作放到后端。 但是该方案有如下 缺点 : 1. 开销 :将解析工作放到后端做依然存在计算和IO开销的问题; 2. 用户体验 :虽然试图通过Grape提供前端展示的,但是用户体验依然不尽如人意,因为不能展示在用户编辑过程中进行反馈。亦即,用户编写剧本和用户提交剧本得到反馈这两个场景是剥离的,这样我认为很不好。 考虑到如上缺点,便把这一版直接都丢了。残余代码还没删,于近日push到了onboard下的 ruby treetop grape 分支。值得一提的是,其中并没有grape的代码,因为再实现了语法解析之后,在进一步调研grape的时候觉得会有如上问题就把该方案丢了。 JAVASCRIPT + EMBER.JS + PEG.JS 该技术方案 变革巨大 ,选用了Ember.js和PEG.js。Ember.js是一个构建ambitous web应用的重量级的前端MVC框架,PEG.js是一个Javascript的解析器生成器,它基于解析表达式语法形式化。 这一套方案的变革在于两点:它将 解析工作放到了浏览器里 进行,彻底解放了后台;此外,开发核心也 不再优先注重于语言 ,而是首先从用户的交互上开始。 该方案的期待场景是,用户在浏览器里面编辑,无需与后台交互便可以进行语法语义检查,而且借助于前端的渲染,可以很友好地展示出错误或当前写作的摘要等。 但是该方案有如下缺点: 1. 开发困难 :在前端实现富文本的编辑器太困难了,试图通过前端组件化来实现,但是…… 因为困难巨大,所以便把这一版直接都丢了。残余代码还没删,于近日push到了onboard下的 emberjs pegjs 分支。注意,这一版本首先构建的是展示部分,实现了故事的信息展示,在准备加入PEG.js的时候,想不出以后该怎么实现前端的 友好 展示,便把该方案丢了。 COFFEESCRIPT + ATOM + LINTER + PEG.JS 既然自己写不出来前端的富文本编辑器,拿一个现成的,然后再基于这个现成的开发适用于我的语言的插件,从而实现友好交互总是可以吧。感谢 github 。 Atom是一个基于chrome V8引擎的现代的可扩展的编辑器;Linter是一个Atom的包,依赖于这个包可以开发自己的实现自己定制语法的on fly解析和错误提示;开发Atom的包主要语言是Coffeescript(用起来超爽无比,但愿能跟ES6兼容)。 那么,这种技术方案的愿景是:用户在atom编辑器里写着故事,一边写的时候,如果出现不规范的地方会有红线标注的错误提示;写完后没有错误提示,一个快捷键就把解析好的文章(json)传到后端服务器。因此可以说,这一方案 完美地解决了用户的交互过程 , 将编辑与解析合二为一 ,同时 又不占用后台资源 。 但是,这一方案依然存在一个弊端: 1. 学习成本 :用户凭什么要学习一个新的语法,能够不能简单简单再简单 因为语法如何构建的问题,在初步实现了展示on fly的功能(fake错误)后,便把这一版丢弃了。关于这一版本的代码,onboard下 language story 分支是用于实现一个新的语言类型,用于通知liner进行解析,而另有一部分代码已经找不到了。 COFFEESCRIPT + ATOM + COMMONMARK + VUE.JS 光阴荏苒,还有三周结课了。为了解决上述的问题,选用了markdown语法的一个超简单的子集作为我的语法,以及Commonmark作为解析工具实现语法的解析。Vue.js用于实现插件展示层和模型层的分离。 该超简单的语法只包含下列三种标记: 是故事的名字 是场景的名字 1. 2. ... 并配合以 () 内链接 的方式实现跳转 综上所述,以上五种技术方案的演变大致是: 从 后端 向 前端 转移 从 语言构造 向 用户体验 转移 所以,虽然最后的方案看起来很简单,但是也是经历了多种考虑后的逐渐演变的结果。而且,最后一种方案目前我认为依然有可以进一步改进的地方,比如Vue.js的选择可以再斟酌,因为不需要数据双向绑定,单项绑定即可,也许可以用Handlerbars.js来替换等等。此外,语法部分还可以再借用markdown的语法进一步增强。 下面介绍最后一种方案的技术细节。 技术细节 代码组织结构 Atom插件开发基于npm规范,主要文件与文件夹如下: lib/ spec/ keymaps/ template/ styles/ menu/ package.json 其中,package.json文件定义插件的依赖、名称、描述等信息。lib文件夹存放主要代码,spec文件夹存放测试,keymaps、styles、menu文件夹是atom插件特有的,分别用于存放快捷键映射、样式、菜单栏信息等文件,template文件夹用于存放Vue.js的渲染模板。 主要文件及功能介绍 lib/parchment.coffee与lib/story/ .coffee parchment主要功能将commonmark解析后得到的ast删减,得到一个子集的解析,解析后的内容包括三个信息,分别是story,plot,option,这三个分别定义在lib/story/ 下;此外,parchment还将解析好的story,plot,option以json的形式发送到后端服务器。 lib/quill.coffee与lib/quill view.coffee 这两个文件主要实现了插件的运作功能,包括展示当前编辑的summary信息以及向后台提交完成的故事信息。quill定义了行为,quill view定义了向用户展示的UI。 lib/vue summary model.coffee与template/summary view.html 考虑到quill view实现UI的方式是通过DOM操作,为了提高效率,采用了Vue.js实现数据和展示分离。这两个文件分别是Vue的模型层和展示层。 sepc/ .coffee 这部分代码是基于Jasmine的BDD测试代码。其中,为了测试http请求,采用mitm.js mock工具,用于拦截http请求并返回测试结果。 敏捷实践 快速迭代 上述讨论的五个方案,基本每个迭代周期为2~3周,或者更准确地说,基本上存活周期为2~3周。利用2~3周时间快速搭建原型,并进一步思考下一步该如何去做的时候,便能够很快地发现这种方案下的困难,如果能克服就可以再做下去,但实在不行就可以毫无顾忌地直接抛弃。 测试驱动开发与重构 所有方案,除了ember.js相关的没有写测试外,其他所有方案基本上实践了测试驱动开发。测试驱动开发在这里并没有流于形式,因为我常常会重构代码,或者尝试乱七八糟的各种功能,测试驱动开发在这里就做了一个保障,确保无论怎么修改,只要能跑过测试,基本上该atom插件是可以work的。这大概也是为什么敏捷宣言里面敢说『拥抱变化』,这是因为有测试在保证吧。 服务器模块 负责人:邢亮 主要工作 主要负责服务端功能的实现以及Devops的相关工作。 服务端的工作有: 内置自动机逻辑令用户可以使用公众号进行文字交互式游戏 传输给前端需要的游戏数据 接收json数据来创建游戏 负责服务端开发的同时,邢亮也负责Devops的相关工作,主要体现在: 阿里云服务的配置 持续部署服务 Crash Report服务 容器管理服务 技术介绍 SPRING BOOT 我们的服务端使用spring boot微框架来搭建。spring boot可以说是一个非常棒的框架,作为一个规模不大的服务端软件来说,使用spring boot进行搭建会节省很多的人力成本。 它本身具有嵌入式的应用服务器,支持打包fat jar,在部署时仅仅需要一个Java虚拟机使用java jar即可启动服务器,方便快捷。 另一方面,spring boot完美的继承了spring 框架的特征,对AOP,IOC熟悉的开发人员可以很快上手。 JPA 引入spring boot的同时,我们也引入了官方推荐的spring jpa ORM框架,它可以通过代码来关联数据库表,不需要写繁杂的配置文件及SQL脚本,减少了我们很多的工作量和维护成本。 DOCKER docker是最近非常火爆的虚拟化技术,我们引入docker来将我们的服务构建成docker镜像,大幅度的减轻了我们服务部署、回滚、维护的压力。 我们会在下一节进一步介绍我们对docker的应用。 模型设计 为了将游戏形式化,我们将之抽象为一个有向图,用户通过遍历有向图即可完成游戏进程。对于有向图的每个点,我们将之称之为游戏段落。用户在每个段落中可以进行选择,根据不同的选择他会踏向不同的后续段落,如果这个段落对应的节点的度只有1的话,那么游戏就结束了,可能是好结局也有可能使BAD END. 为了让关系型数据库可以存储有向图,我们将有向图进一步进行了抽象,将之分为几个Model,分别为: Game 游戏 point 段落 Selection 选项 Line 段落跳转 User 用户 Game 中主要存储有游戏id,游戏名称,创建者的id,创建时间,以及第一个段落的id。 Point 中主要存储了段落id,段落文本,对应游戏id,创建时间以及是否已经结束。 Selection 中主要存储了选项id,选项文本,对应游戏id,及对应段落id。 Line 中主要存储来源段落id,目标段落id,游戏id,及这条线需要的条件即用户当前及过去所选择的选项。 User 中主要存储了用户信息,及他所处的段落,以及他选过的选项等。 完成模型的设计后,我们就可以将游戏存储在数据库当中,并方便随时将之转化为有向图进行遍历。 微信接口的使用过程 在设计自动机过程中的一个主要工作就是与微信公共平台的通信了。微信使用XML作为通信格式。 为了使用微信接口,我们注册了一个微信公共号 文字游戏云 ,申请通过后腾讯给我们一个key,方便我们与之通信。 在接口调用前,腾讯需要验证我们的服务器是否正常运行,它会向服务器80端口的某个url发送一个GET请求,附带有时间戳等信息,我们将我们的令牌,时间戳组装成一个string,并使用SHA 1进行签名,与请求进行比对,如若无误我们返回请求的一个字段即可完成验证。 这部分操作主要是腾讯为了方便我们验证微信公共平台身分所设立的操作。 在后续的通信过程中,它都会用这一操作做为第一步。 如果微信公共号收到了用户的一条消息后,它会进一步的向服务器相同url的地址发送POST请求, 附带XML编码的用户消息。 用户消息中主要有用户的微信号,消息类型,内容等。我们就可以使用这部分信息进行自动账号注册, 游戏进程控制等功能了。 游戏进程控制 为了完成游戏进程控制,我们将用户的状态分为: 新用户(NEW_USER) 老用户又来玩(JUST_COME) 选择游戏(SELECT_GAME) 游戏中(GAMING) 在用户发送消息后,我们会根据它发送信息的属性,判断他所处的状态。比如这个用户从来没有发送过信息,我们就会将之归类为新用户。比如他上次发送信息与这个发送信息时间差距太久,我们就会将之归为JUST_COME。其他状态会根据游戏进程的不同情况进行判断。 获取状态后,我们根据不同状态会指派不同的服务来处理用户的请求,比如它处在游戏状态中,我们就会使用游戏自动机来判断他的下一个段落是什么,或者游戏是否结束等等。 DEVOPS 负责人:邢亮 代码管理 我们使用了Git来管理代码,成员的代码都会提交到onboard当中。与传统方式不同的是,我们有四位成员,我们会使用不同分支来管理各自的代码。 持续部署 我们使用阿里云作为我们的服务器。我们引入了Jenkins来做持续部署工具。 首先我们使用Maven Docker插件,来构建我们服务的镜像。Jenkins检测到代码仓库有变动时,会使用 mvn clean package docker:build 命令完成服务镜像的构建,并放置在本地docker仓库。接下来Jenkins会将本地启动的上一版本的容器删除,并使用docker run指令新建一个容器将最新的服务跑到docker引擎中,并暴露相应的接口。完成持续部署过程。 CRASH REPORT管理 我们搭建了Sentry服务来帮助我们进行Crash Report的管理,当我们的引擎服务有日志输出时,判断它的级别,如果是warning以上级别,我们会将日志发送到Sentry当中,并通知我们这些开发人员,出现了bug。之后我们会根据情况及时修复。 容器管理 我们引入了希云管理我们的docker镜像和容器,持续部署过程是有风险的,我们经常会出现部署的版本有bug的情况,所以我们需要有迅速回滚的能力,我们在本地保存了5天内持续部署过程生成的镜像,并将之存储在本地仓库中,让希云管起来。 当我们需要迅速进行回滚时,我们仅仅需要删除现在的容器,并启动生成上一版本的容器即可完成回滚。 前端页面 负责人:靳灿灿 功能目标 首先介绍下前端页面实现的功能目标。 前端部分实现的主要功能是向后台请求游戏数据(json格式),实现在手机和pc端的浏览器网页上展示游戏列表,并正确渲染每个小游戏。 对于该功能,需要实现以下基本需求: 一个单页面应用即可, 游戏列表界面,点击选择游戏。 友好地适配各种手机终端。 参考橙光游戏的游戏界面,希望故事情节的展示具有逐字打印的效果 考虑一些提高功能:好感度,插图,配乐等,提高用户体验。 项目进展回顾: 以中期检查为时间点,中期之前,我通过jquery,css,html5等基础前端语言,实现了一个只有四五个章节的超简易的小游戏的页面渲染。该游戏可以在手机及pc端浏览器页面上正确运行,并且故事情节的展示具有逐字打印效果。游戏界面如图1。 图1:游戏界面 逐字打印效果的实现原理是先隐藏故事段落,根据字数长度,定时调用追加一个字符的函数,直至段落完全显示。 图2:逐字打印效果代码 此后,开始尝试新的故事剧本,很快发现代码逻辑不够清晰,段落跳转频频出错。此时接触到angularjs的双向数据绑定特性(bi directional data binding)。例如,在Input里设置属性ng model=”yourcontent”将文本输入框绑定到名为yourcontent的模型变量。在其他位置使用改模型变量就会得到该变量的实时数据,输入框的任何更改会立即反应到模型变量上。angularjs通过作用域来保持数据模型和视图界面UI的双向同步,一旦模型状态发生改变,angularjs会立即刷新反映在视图界面。与上面通过jquery实现的代码相比,使用angularjs,可以不再需要专门注册事件侦听器或者添加事件处理程序,减少了大量代码,极大地节约开发时间。 鉴于此,后期使用angularjs对前端代码进行了重构。页面核心代码如图3。通过用户不同点击行为,改变当前展示的故事段落。同时,对页面的样式等也做了调整,最终的页面较之之前更加美观,适配性也更好。访问地址: http://115.28.225.159/ http://115.28.225.159/ 。 图3:angualrjs核心代码展示 敏捷实践 根据上面的介绍,项目开发中接触到了代码重构(refactor)。项目开发前期注重功能的实现,完成了需求之后,却发现代码不精炼,冗余且结构混乱。通过重构,系统运行的正确性没有受到影响,但是实现的代码更精炼和易于维护了。借助angularjs的数据双向绑定,页面的跳转可以简单地通过绑定变量实现,大大精简了代码。 单元测试 负责人:周鸿博 JUNIT和MOCKITO 对于JUnit,这里就不详细介绍了,网上的教程有很多,比如 这个 http://www.cnblogs.com/yangxia test/p/3991572.html 和 这个 http://huihai.iteye.com/blog/1986568 。 下面主要介绍一下 Mockito 。 什么是mock测试,什么是mock对象? 先来看看下面这个示例: 从上图可以看出如果我们要对A进行测试,那么就要先把整个依赖树构建出来,也就是BCDE的实例。 一种替代方案就是使用mocks 从图中可以清晰的看出: mock对象就是在调试期间用来作为真实对象的替代品 mock测试就是在测试过程中,对那些不容易构建的对象用一个虚拟对象来代替测试的方法就叫mock测试 模拟的好处是什么? 提前创建测试; TDD(测试驱动开发) 如果你创建了一个Mock那么你就可以在service接口创建之前写Service Tests了,这样你就能在开发过程中把测试添加到你的自动化测试环境中了。换句话说,模拟使你能够使用测试驱动开发。 团队可以并行工作 这类似于上面的那点;为不存在的代码创建测试。但前面讲的是开发人员编写测试程序,这里说的是测试团队来创建。当还没有任何东西要测的时候测试团队如何来创建测试呢?模拟并针对模拟测试!这意味着当service借口需要测试时,实际上QA团队已经有了一套完整的测试组件;没有出现一个团队等待另一个团队完成的情况。这使得模拟的效益型尤为突出了。 你可以创建一个验证或者演示程序。 为无法访问的资源编写测试 这个好处不属于实际效益的一种,而是作为一个必要时的“救生圈”。有没有遇到这样的情况?当你想要测试一个service接口,但service需要经过防火墙访问,防火墙不能为你打开或者你需要认证才能访问。遇到这样情况时,你可以在你能访问的地方使用MockService替代,这就是一个“救生圈”功能。 Mock 可以分发给用户 隔离系统 知道什么是mock测试后,那么我们就来认识一下mock框架 Mockito。 Mockito区别于其他模拟框架的地方主要是允许开发者在没有建立“预期”时验证被测系统的行为。 MOCKITO入门实例 Maven依赖: Xml代码 ...

javascriptspringboot敏捷开发文字游戏引擎

0

点赞

0

回复

849

浏览

优秀的代码 vs. 糟糕的代码

可持续开发不仅在于项目架构设计,还与代码质量密切相关,代码的整洁度和质量成正比。—— Robert C. Martin, "Clean Code" 如果你还没有发现代码质量的区别,如果你从未见过优秀的代码,或者从未见过糟糕的代码,那么本文将以直观地对比,告诉你代码质量究竟会有多大的区别。 代码量 我们知道代码量显示着功能的复杂程度,例如Windows XP的代码量超过2000万行,Linux内核有1500万行(2012年)。然而代码量和功能数量之间并非线性关系。 优秀的设计中,代码量和功能数的关系是这样的: 糟糕的设计中,代码量和功能数的关系是这样的: 优秀的系统往往会有优秀的结构设计:层次清晰、职责单一、模块化,方便扩展或者复用。功能的添加往往只是在现有框架中添加少量代码。 然而糟糕的设计中,层次混乱、互相耦合、难以阅读,既难以复用又不易扩展。每当被要求添加功能时,不得不几乎完整地实现整个功能的流程,并修复与原系统的所有兼容问题。 注释 优秀的代码中,注释是这样的: 糟糕的代码中,注释是这样的: 最愚蠢的程序员都能写出机器能读懂的代码,而优秀的程序员能写出人可以读懂的代码。 程序的注释是为了让人读得懂。多数优秀的代码中,注释几乎接近代码行数的一半,描述函数功能、解释参数配置、指出陷阱所在。而糟糕的代码中不仅不含这些注释,甚至会保留大量残余代码,可读性差又难以重构。 命名 优秀的代码中,命名是这样的: 糟糕的代码中,命名是这样的: 命名是为了让代码更加容易阅读,使用规范的术语不仅更加易懂,同时也是开发者知识水平和开发经验的表现。如果说以中文拼音命名显得奇怪,那么直接以 a , b , c , d 命名的便会显得可恨,谁记得你的 a 是神马东西! 冗余 看一个经典的例子,字符串替换: 稍微有点 Javascript http://tianmaying.com/tutorials/tag/javascript 常识的便会想到正则表达式: 何必拷贝这一堆的代码给老板看呢?开发中确实有很多时候,直接拷贝代码既能立竿见影地完成功能,又不会影响原有功能。但这样的代码多起来之后,万一功能调整你便需要重新debug所有的副本。花一些时间学习更优雅的用法是值得的。 函数 保持函数的短小,使你的代码更加易读,例如: 将复杂的逻辑分步骤完成。如果写在一起将会是这样的: 相信我,一旦你写了这样的函数,它会随着时间的推移变得越来越长,直到有一天,你也忘了其中的一段代码到底是做什么的。 事实上,复杂的函数不仅可以按照步骤划分,更应按照层次来细化。不要在一个函数中进行不同层次的操作,否则它会变得非常难懂。 同时发表在: http://harttle.com/2015/04/13/elegant and bad.html http://harttle.com/2015/04/13/elegant and bad.html ...

javascript

0

点赞

0

回复

849

浏览

打造前端开发的Vim环境

前不久harttle着手搭建了 类似IDE的Vim环境 http://tianmaying.com/tutorial/vim ide ,然而对于前端开发者这还远远不够。 本文便记录一下如何实现前端开发者关心的那些特性:不同文件类型的缩进、HTML标签匹配注释移除、HTML/CSS/Javascript自动补全、代码风格检查等等。让我们来打造一个适合前端开发的 Vim http://tianmaying.com/tutorials/tag/vim ! 文件类型设置 前端文件有不同的类型,而每个人对于缩进又有不同都要求。Vim中我们可以为每种文件设置不同的缩进和Tab行为。 查看当前文件类型 使用 :set filetype 命令可以查看当前文件的类型。例如: filetype=html.handlebars...

bashjavascripthtmlcssvim

0

点赞

1

回复

897

浏览

各种语言常见的包管理工具

包管理和构建系统是现代的软件开发团队中必不可少的工具,也是Linux软件系统的常见组织方式。 现代编程语言大多给出了自己专用的包管理和构建系统,那么本文便来总结一下小编用过的那些包管理和构建系统。 JAVASCRIPT 服务器端 Javascript 通常使用 NPM http://npmjs.org/ 作为依赖管理工具。 通过NPM提供的 npm 命令来进行依赖的下载、升级和移除。 通过 package.json 来定义软件包的元信息、开发依赖(开发或测试需要)、部署依赖(运行时需要)。 依赖递归地存储在 node_modules 中。 依赖在项目之间是隔离的,全局安装( g )会使它成为命令行工具而不是全局依赖。 递归的依赖下载风格使得NPM的缓存及其重要。缓存位于 ~/.npm 下,这里保存这 .tgz 格式的包文件。 JavaScript 通常使用 Grunt http://gruntjs.com/ 进行构建。 Grunt通过插件来完成任务,每个插件相当于Makefile的一个命令。 Grunt任务定义在 Gruntfile.js 中。 NPM提供了众多的Grunt插件,当然你也可以手写。 Grunt任务继承了JavaScript的异步特性。 前端 LIB 前端 lib 可以通过 Bower http://bower.io/ 来下载。Bower 不仅可以下载已注册的软件包,还可以下载Github Repo,甚至是一个文件的URL。 通过 bower 命令进行依赖管理。 bower.json 定义了软件包的元信息与依赖。 依赖所在路径可以在 bower.json 中进行设置。 Bower只是一个命令行工具,你需要在正确的路径执行Bower命令。 Bower可以灵活地下载各种依赖,但它的缺点也是明显的:未注册的软件包往往包含冗余的非生产环境的代码,有时甚至需要手动构建。 JAVA Maven https://maven.apache.org/ 是基于项目对象模型(POM)的项目管理工具,通过文本文件来描述项目的版本、URL、License、依赖等信息。 通过 pom.xml 来描述Maven项目。 Maven是项目管理工具,其功能包括依赖管理、软件构建。 Maven通过 .properties 文件和命令行参数读取系统特性。 Ant http://ant.apache.org/ 是由Java写成的编译、测试和部署Java应用的命令行工具。 通过 build.xml 来描述构建过程。 build.xml 的逻辑类似Makefile,每个target由命令和参数构成。 通过命令行参数来读取系统特性。 Gradle https://zh.wikipedia.org/wiki/Gradle 是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的XML。 PYTHON Python的包管理工具能讲一整天的故事:distribute、setuptools、distutils、easy_install、pip、distutils2、packaging、eggs、wheels、pyvenv、virtualenv…… 这也是我为什么讨厌安装python软件的原因,与此同时,旧版本的python2比新的python3更加流行以及两者不兼容也常常给Linux包管理造成麻烦。 现在开始讲故事: 1. distutils是python的标准库; 2. setuptools试图完成distutils缺少的特性而开始开发; 3. easy_install是setuptools的命令行接口,有更多的特性; 4. 在setuptools的开发过程中产生了分歧,于是出现了distribute,它fork自setuptools,并在2013年取得和解并重新merge到setuptools 0.7版本; 5. 相比于上述工具, pip http://pypi.python.org/pypi/pip 是一个更加高阶的接口,pip成为事实上的python软件的安装工具; 6. eggs和wheels成为事实上的二进制python软件的安装工具。 venv 和 virtualenv 则是用来创建python虚拟环境来实现应用隔离的。 venv https://docs.python.org/3/library/venv.html 在python3.3中引入,用于在自己的目录下创建轻量级的虚拟环境,也可以孤立于系统环境。 virtualenv http://virtualenv.readthedocs.org/ 则是一个python软件包,用于创建孤立的python环境。 RUBY Ruby的软件包单元为 RubyGem https://rubygems.org/ 。 Gem由 .gemspec 文件描述。 Gem的构建过程由 Rakefile 描述。 Rake https://rubygems.org/gems/rake 是Gem的构建工具,它与Make类似,用以完成自动化测试和代码生成。 Bundle http://bundler.io/ 则是Ruby的包管理工具,用来跟踪和下载正确版本的Gem。 同时发表在: http://harttle.com/2015/05/29/pkg manager.html http://harttle.com/2015/05/29/pkg manager.html ...

pythonjavarubyjavascript

0

点赞

0

回复

944

浏览

AngularJS 数据绑定与 $digest 循环

数据绑定可以说是 AngularJS http://tianmaying.com/tutorials/tag/angularjs 最大的特色。在Angular中,视图和模型的数据不仅是双向绑定的,并而且是实时的。 使用Angular可以做到良好的甚至是神奇的用户体验,例如用户在输入表单的过程中实时地提示输入有误或者输入正确。 双向绑定 下图是模板引擎中常见的单向数据绑定: 通常在服务器端,将数据模型和模板结合,生成视图。当视图中的数据发生改变时,数据模型不会自动更新;模型发生改变时,视图也不会自动刷新。 因此开发者不得不写大量的代码来同步视图和模型。例如: 视图 模型:绑定DOM事件来监听视图的改变,进而通过javascript函数来同步数据模型,更改javascript对象,或者发送HTTP请求到后台。 模型 视图:模型改变时通过jQuery操作来更新DOM。如果数据模型在后台,可能还需要websocket之类的推送机制。 而Angular提供了双向的数据绑定,我们可以在Angular Controller的 $scope 中声明数据模型,在模板中进行绑定。 Angular会自动添加DOM事件,并在 $scope 发生改变时自动进行DOM操作。下面是Angular双向绑定的MVT关系示意图: 图片来源: https://docs.angularjs.org/guide/databinding https://docs.angularjs.org/guide/databinding SCOPE scope 在Angular中代表着应用模型,它是模板中表达式的上下文。在 scope 中,你可以 watch (监听)表达式值的变化,可以传播事件。 在编写控制器时,我们往往会注入一个 $scope Service来设置当前模板的上下文: var app = angular.module('helloWorldApp', );...

javascript数据绑定angularjsmvc

新建主题 记录代码

学员动态

  • A**i向课程作业中提交了代码
  • 天码君评论了三年在课程中的作业:你的代码应该是可以跑...
  • 三年在课程中提出了问题:请老师解答下这课作业...
  • 天码君回复了L**z在课程中的问题:欢迎来到天码营学习,...
  • L**z向课程作业中提交了代码
  • L**z向课程作业中提交了代码
  • z**x在课程中提出了问题:老师,为什么它那个中...
  • z**x评论了:安装的时候出现这个界...
  • 三年添加了笔记:关于MyBatis本...
  • L**z向课程作业中提交了代码
  • D**n在课程作业中回复了老师:这道题是什么意思? ...
  • L**z向课程作业中提交了代码
  • D**n向课程作业中提交了代码
  • A**i向课程作业中提交了代码
  • c**u向课程作业中提交了代码
  • A**i向课程作业中提交了代码
  • z**x添加了笔记:public sta...
  • 天码君回复了c**h在课程中的问题:欢迎来到天码营学习,...
  • w**3回复了z**x在课程中的问题:安装java8 不要...
  • 三年添加了笔记:分析我们的业务场景,...
  • 天码君回复了D**n在课程中的问题:欢迎来到天码营学习,...
  • 天码君回复了D**G在课程中的问题:欢迎来到天码营学习,...
  • 天码君回复了工**武在课程中的问题:欢迎来到天码营学习,...
  • 天码君回复了X**E在课程中的问题:欢迎来到天码营学习,...
  • c**6向课程作业中提交了代码
  • w**3评论了z**x在课程中的作业:对的,一般是在包下的...
  • z**x在课程作业中回复了老师:所以说,是要先创建包...
  • L**z在课程中提出了问题:老师,刚才我换了一个...
  • A**i向课程作业中提交了代码
  • 三年添加了笔记:需要修改MyBati...
  • z**x在课程中提出了问题:好的老师,那版本11...
  • 三年添加了笔记:先来定义Mapper...
  • L**z在课程中提出了问题
  • w**3回复了L**z在课程中的问题:嗯,毕竟几乎公司和学...
  • 扶**学向课程作业中提交了代码
  • w**3评论了z**x在课程中的作业:...
  • 工**武在课程中提出了问题:请问为什么课程的视频...
  • w**3评论了z**x在课程中的作业:这是一个类的主体部分...
  • z**x在课程作业中回复了老师:想请问下老师这行代码...
  • 三年添加了笔记:这里你注意两点即可:...
  • z**x在课程中提出了问题:老师我把Java换成...
  • J**y向课程作业中提交了代码
  • z**x在课程作业中回复了老师:入门到精通这本我今天...
  • 天码君回复了s**n在课程中的问题:欢迎来到天码营学习,...
  • X**E向课程作业中提交了代码
  • 白**2向课程作业中提交了代码
  • w**3回复了z**x在课程中的问题:可以设置字体 htt...
  • 天码君回复了z**x在课程中的问题:欢迎来到天码营学习,...
  • w**3评论了z**x在课程中的作业:这个是根据你的jav...
  • w**3回复了z**x在课程中的问题:重新安装对应版本的e...
  • L**z在课程中提出了问题:好的那我还是装回我的...
  • L**z向课程作业中提交了代码
  • J**y向课程作业中提交了代码
  • w**3回复了z**x在课程中的问题:百度找一下如何彻底删...
  • D**n向课程作业中提交了代码
  • A**i完成了课程的作业
  • z**x在课程中提出了问题:哇,我换成课程里的压...
  • A**i向课程作业中提交了代码
  • 天码君回复了h**2在课程中的问题:欢迎来到天码营学习,...
  • 天码君回复了A**i在课程中的问题:欢迎来到天码营学习,...
  • 三年在课程作业中回复了老师:老师,我的代码中报这...
  • z**x在课程中提出了问题:请问下老师,安装的时...
  • w**3回复了z**x在课程中的问题:软件安装问题每个人碰...
  • J**y向课程作业中提交了代码
  • c**u向课程作业中提交了代码
  • 天码君回复了R.D在课程中的问题:欢迎来到天码营学习,...
  • L**z向课程作业中提交了代码
  • w**3回复了L**z在课程中的问题:你如果要用java1...
  • A**i向课程作业中提交了代码
  • J**y向课程作业中提交了代码
  • z**x在课程中提出了问题:对应版本?我的JDK...
  • z**x在课程作业中回复了老师:所以说包的作用就像是...
  • J**y向课程作业中提交了代码
  • c**u评论了:13课实战弄不明...
  • c**6向课程作业中提交了代码
  • R.D完成了课程的作业
  • J**y向课程作业中提交了代码
  • w**3评论了D**n在课程中的作业:看清楚要求,参数表要...
  • J**y向课程作业中提交了代码
  • D**n向课程作业中提交了代码
  • z**x在课程中提出了问题:老师我的电脑是64位...
  • 天码君回复了A**i在课程中的问题:欢迎来到天码营学习,...
  • L**z向课程作业中提交了代码
  • 三年添加了笔记:接下来引入MyBat...
  • z**x添加了笔记:将希望输出信息放到S...
反馈意见