Phaser3初体验

译者说:

1、本文讲述的是作者初次使用Phaser3(之前都用Phaser2)制作游戏的一些心得和碰到的坑。

2、为了跟代码对应,有些单词不译成中文(比如scene、image)。

3、初次翻译教程,加上水平所限,疏漏之处欢迎指教。

4、原文分为三部分,链接在此:原文1、原文2、原文3

5、译者:大吃货,转载请注明。

6、Phaser2还没学会就出了Phaser3…不带这么坑爹的。

===========================================================================

Part1:

项目介绍这个项目故意做得比较小,所以我不会在学习过程中讲得过于详细。我将它设定为一个休闲益智游戏,可通过触屏输入(或单击)。这是一个很多人从小就知道的纸牌游戏----翻牌记忆大作战。游戏的名字就叫Pixel Memory吧。

概述:挑战Phaser3

本文的重点是向大家展示我使用Phaser3的过程,并分享我的一部分代码。在我使用Phaser3的第一天,我遇到了以下的挑战,我将在下文一一阐述。

1、Phaser 3 游戏设置

2、设置全局变量

3、添加scene

4、游戏的闭包(原文enclosing,个人觉得“闭包”比“打包”更适合)

5、包含场景和预设的独立文件

6、Canvas的居中和尺寸调整

7、全屏化

8、设置Bitmap Text 的原点

1、Phaser 3 游戏设置

第一个挑战是:我该怎样用一种首选的配置来启动游戏?

在Phaser3中用一个对象作为游戏的构造器,这个对象包含了游戏的各种配置属性。

这些属性是很重要的,从官网的例子就可以看出来。

var config = {// ...
};
var game  = new Phaser.Game(config);

你会发现很多的新属性,它们非常酷!其中之一就是游戏的name。parent属性就是包含了游戏canvas的div的ID。

下面就是我的游戏设置对象

var config  = {type      : Phaser.AUTO,width     : 9  * 64,           // 576height    : 15 * 64,                     // 960parent    : 'phaser-app',scene     : scenes,title     : 'PixelMemory'
};var game  = new Phaser.Game(config);

2、设置全局变量

在Phaser2中我常做的一件事就是设置一个可以从每个scene调用的全局变量(在Phaser3中state变成scene)。如果我需要从许多场景中访问一个属性,我选择设置全局变量,而不是在场景之间传递它们。此外,在场景之间传值目前还存在bug。见github。

下面看看我是怎样设置全局变量的:

game._URL = 'http://localhost/PhaserGames/PixelMemory/'
game._USER_ID = 0;

稍后在任意场景中调用这个变量的方法:

var url  = this.sys.game._URL;
var u_id  = this.sys.game._USER_ID;

3、添加scene

在Phaser3中,我们把scene添加到一个数组中,再把这个数组添加到config对象的scene属性中。虽然我喜欢这种用数组传递的方式,但最开始我还是有点困惑:scene的启动顺序是否跟它在数组中的顺序有关呢?

此外,如果你添加了很多的scene,这种方式的可读性就会变差。为了提高代码的可读性,我通过这种方式添加scene:

var scenes  = [];scenes.push(BootScene);
scenes.push(PreloadScene);
scenes.push(IntroScene);var config = {// ...scene: scenes,// ...
};

除了你添加的第一个scene,其他的scene顺序无关紧要。默认情况下,游戏将自动从数组的第一个scene启动。请注意你添加的第一个scene。当然,你也可以手动设定为从任意的scene启动游戏。

4、游戏的闭包

浏览器端游戏跟原生应用不同之处在于,前者的代码很容易在浏览器上暴露出来。我喜欢尽可能的把函数闭包,这样任何人想要在游戏运行时修改游戏就变得不那么容易(破解、修改)。基于以上原因,我一直喜欢把游戏进行闭包。我不知道这能多大程度保护游戏,但聊胜于无。我相信,你做出的这些努力,会让玩家感受到你对你的游戏有多在乎。(我总是想看看自己能否轻易地破解H5游戏,像我这样的人肯定不少)

在Phaser2中,我用这种简单的方式实现游戏闭包:

var App = function() {};App.prototype.start = function()
{// Scenesvar scenes = [];scenes.push(BootScene);scenes.push(PreloadScene);scenes.push(IntroScene);// Game configvar config  = {type      : Phaser.AUTO,width     : 9  * 64,           // 576height    : 15 * 64,                     // 960parent    : 'phaser-app',scene     : scenes,title     : 'PixelMemory'};// Create game appvar game  = new Phaser.Game(config);// Globalsgame._URL = 'http://localhost/PhaserGames/PixelMemory/';     // this.sys.game._URLgame._USER_ID = 0;game._CONFIG = config;
};window.onload = function()
{'use strict';var app = new App();app.start();
}

5、包含场景和预设的独立文件

出于代码组织的原因(也可能是个人喜好),我喜欢为每个scene和预设建立单独的js文件。闭包和原型(prototyping)帮助我保持一个干净的命名空间。此外,如果我的代码整齐地组织在这样的文件中,我会更好地专注于当前的任务。

根据官网的例子,最好在场景中使用静态方法,而不是把它们原型化。之前传给scene对象的name字符串,以后可以在启动这个scene时直接调用(或切换,Phaser3的新功能)。

下面就是我组织scene的方式:

var PreloadScene= new Phaser.Scene('Preload');
PreloadScene.preload = function()
{'use strict';// ...
};
PreloadScene.create= function()
{'use strict';// ...
};
PreloadScene.update= function()
{'use strict';// ...
};

这个是预设文件:

var Helper = function() {};
Helper.prototype.createText = function(ctx, x, y, string, size, anchor)
{'use strict';
};

如何从当前场景开始一个新场景:

this.scene.start('Preload');

6、Canvas的居中和尺寸调整

在Phaser2中我们可以很方便地配置canvas的位置和尺寸。但Phaser3中还没有类似的方法。所以我得用老办法,在index.html中添加一些css和js代码。

对于这个游戏,我希望它始终在屏幕居中,并随着浏览器窗口调整大小,并且可以适配移动端。

下面是我的css代码(‘phaser-app’是我在config对象中配置的parent属性)

body {margin: 0;overflow: hidden;background-color: black;
}canvas {height: 100%;
}#phaser-app {margin: 0 auto;
}

js代码:

// Resize
function resizeApp()
{var div = document.getElementById('phaser-app');                    div.style.width = window.innerHeight * 0.6;div.style.height = window.innerHeight;
}
window.addEventListener('resize', function(e)
{resizeApp();
});
resizeApp();

7、全屏化

在Phaser2中我们可以方便地使游戏全屏启动。但在Phaser3Beta20中似乎没有这样的功能(至少原文作者没找到这个功能),所以我必须手写全屏代码。

此代码在我的index.html文件中:

// Fullscreen
function fs_status()
{if(document.fullscreenElement){return true;}else if(document.webkitFullscreenElement){return true;}else if(document.mozFullScreenElement){return true;}else{return false;}
}
function goFullscreen()
{if(fs_status()){return;}var el = document.getElementsByTagName('canvas')[0];var requestFullScreen = el.requestFullscreen || el.msRequestFullscreen || el.mozRequestFullScreen || el.webkitRequestFullscreen;if(requestFullScreen){requestFullScreen.call(el);}
}
document.getElementsByTagName('div')[0].addEventListener('click', goFullscreen);

8、设置Bitmap Text 的原点

目前为止,我们还无法改变bitmap text的原点,但是可以改变image的原点(没试过其他对象)。

Phaser3舍弃了‘anchor’,取而代之的是‘origin’。

Phaser3默认情况下,image的原点就设置在了0.5(Phaser2是0),但是bitmap text的原点默认还是0。

设置一个image的原点:

// You can chain it directly
this.add.image(0, 0, 'bg-main').setOrigin(0);// Or you can add it later to an image object
this.bg_main.setOrigin(0);

为了把bitmap text的原点设置在中心,我写了这个辅助方法:

// My helper prefab
var Helper = function() {};// Method to create a bitmap text
// I am still calling it "ancho"r instead of "origin"; old habit that will change going forward
Helper.prototype.createText = function(ctx, x, y, string, size, anchor)
{'use strict';var text;          var font  = 'supermercado';var size  = size || 64;// Texttext = ctx.add.bitmapText(x, y, font, string, 64);// Anchor...// ...centerif(!anchor || anchor === 0.5){text.x  -= (text.width * 0.5);text.y  -= (text.height * 0.5);}// ...1if(anchor === 1){text.x  -= text.width;text.y  -= text.height;}// ...x & y differentelse if(typeof anchor == 'object'){if(anchor.x === 0.5){text.x -= (text.width * 0.5);}if(anchor.y === 0.5){text.y -= (text.height * 0.5);}if(anchor.x === 1){text.x  -= text.width;}if(anchor.y === 1){text.y  -= text.height;}}// Returnreturn text;
};

===========================================================================

Part2:

概述:

1、在场景之间传值。

2、改变对象的宽高。

3、配置tween(补间动画)。

4、精灵的子对象:带标签的按钮。

1、在场景之间传值

2018.2.25更新:这个bug在3.1.2中已被修复。详情参考这个帖子,特别是对全局变量的使用。

如上文所说,Phaser3Beta20在场景中传递数据会有bug。正常来说,我们是这样在场景中传值的:

// Here we are in the "Level" scene
// We start the "play" scene and send some data
this.scene.start('Play', { level: 3, difficulty: Medium });// In the init or create method of the "Play" scene you receive the data as follows
PlayScene.init = function(data)
{this._LEVEL = data.level;this._DIFF  = data.difficulty;
};

不幸的是,在PlayScene场景接收到的值是空的。

在我的小游戏中,玩家可以选择难度,还可以指定卡片的颜色。由于我无法把这个值传给PlayScene,我只好使用全局变量来解决这个问题。

在DecksScene场景选择卡片颜色:

DecksScene.clickBlue = function()
{'use strict';if(this.flagClick() === false){return;}this.sys.game._DECK = 0;this.hideUnselected();this.startTransitionOut(this.goPlay);
};

在DifficultyScene场景选择难度:

DifficultyScene.clickEasy = function()
{'use strict';if(this.flagClick() === false){return;}this.sys.game._COLS = 4;this.sys.game._ROWS = 7;this.startTransitionOut(this.goDecks);
};

初始化PlayScene场景时,给它的配置赋值:

PlayScene.init = function()
{'use strict';// ...this._COLS  = this.sys.game._COLS;this._ROWS  = this.sys.game._ROWS;this._DECK   = this.sys.game._DECK;// ...this.sys.game._COLS = undefined;this.sys.game._ROWS = undefined;this.sys.game._DECK = undefined;
};

这个想法很简单,只要你设置的值正确,程序就可以正常运行。

重要提示:如果你想在全局空间保存一个变量,就必须把它保存在this.sys.game里面。

2、改变对象的宽高

Phaser2中你可以直接设置一个游戏对象的宽高,这些数值也会确实地应用在游戏中。

在Phaser3中可就没这么简单了。在Phaser3中,你必须弄清楚sprite.width和sprite.displayWidth的区别。比如,我想动态改变一个玩家配置文件中的经验条宽度,你就必须设定displayWidth属性。

现在width属性被保留为image的宽度。如果一个sprite sheet含有width属性,则它表示的是sprite sheet一帧的宽度值。你也可以通过缩放来改变精灵的宽度。但是请记住,这个操作会缩放整个image!由于我的经验条有长短不同的边,所以我不能缩放整个image。我需要动态地改变宽度,于是操作displayWidth值就可以实现了。

// This does nothing
this.ui.profile.xpbar.width = Math.round(this.ui.profile.xpbar.width * ratio);// This works
this.ui.profile.xpbar.displayWidth  = Math.round(this.ui.profile.xpbar.width * ratio);// This also changes the width, but it scales the full width of the image!!
this.ui.profile.xpbar.setScale(ratio, 1);

3、配置tween

Phaser3的补间动画简直棒极了!

我们只是简单地给一个对象传递了补间动画的配置属性,比如duration、ease,还有callback和callbackScope。

下面这个例子对于了解补间动画属性非常有用,如callbackScope。试试吧!

var scope = this;var tween = this.tweens.add({targets: [ myImage, myGraphic, mySprite ],x: 600,ease: 'Linear',duration: 3000,yoyo: true,repeat: 1, // -1 for infinite repeatsonStart: function () { console.log('onStart'); console.log(arguments); },onComplete: function () { console.log('onComplete'); console.log(arguments); },onYoyo: function () { console.log('onYoyo'); console.log(arguments); },onRepeat: function () { console.log('onRepeat'); console.log(arguments); },callbackScope: scope});

有时你可能创建了一些非常相似的补间动画,它们只有很小的差别。在这种情况下,你就可以创建一个方法,来为你返回补间动画的配置对象。

function getTweenConfig(ctx, delay, col, row, pos)
{return {targets: ctx.decks[col][row],delay: delay,duration: 500,x: pos.x,y: pos.y,angle: -720,ease: 'Linear',// play sfxonStart: function() { ctx.time.delayedCall(delay, ctx.helper.playSfx, [ctx, 'tap_card'], ctx); },// normal callbackonComplete: function() { completeIntro.call(ctx, col, row); },callbackScope: ctx}
}

4、精灵的子对象:带标签的按钮

我喜欢为对象创建辅助方法,我会在我的游戏中多次这么做。

这个对象就是带标签的按钮。虽然很多时候按钮是相同的,但它们的标签从来都不一样。

在Phaser2中,你可以创建一个按钮,并将一个文本作为它的子对象。无论你改变按钮的什么属性,都会应用到它的子对象(文本)上。不幸的是,在Phaser3Beta20中,你不可以给一个对象添加子对象。我希望在以后的版本中可以实现这个功能。虽然你依旧可以创建Phaser组,但是组对象也没有提供我的按钮需要的所有功能。所以,我只好将文本添加到按钮的data对象中。

我会在下面贴出方法的完整代码,你可以用在自己的代码中并进一步完善它:

Helper.prototype.createBtnWithLabel = function(ctx, x, y, img, callback, label_config, frames, data)
{'use strict';var btn;var text;var label_config = label_config || { string: '[n/a]', size: 64, color: '0xFFFFFF', x: 0, y: 0 };// Label positionif(!label_config.x){label_config.x  = 0;}if(!label_config.y){label_config.y  = 0;}// Create...// ...spritebtn  = ctx.add.sprite(x, y, img);// ...labeltext  = this.createText(ctx, x + label_config.x, y + label_config.y, label_config.string, label_config.size, label_config.color);// ...databtn.data = data || {};btn.data.label_obj  = text;// Inputs...// ...activatebtn.setInteractive();// ...callbackbtn.on('pointerup', function(e){ctx.helper.playClickSfx(ctx);callback.call(ctx);});// Frames...// ...hoverif(frames && frames.over){btn.on('pointerover', function(e){this.setFrame(frames.over);});btn.on('pointerout', function(e){this.setFrame(0);});}// ...clickif(frames && frames.down){btn.on('pointerdown', function(e){this.setFrame(frames.down);});}// Return groupreturn btn;
};
    如你所见,这种方法的一个妙处在于,你可以轻松地为所有的按钮添加或者改变点击音效。你还可以为移动设备的播放器添加回调函数,使得你的一个触屏动作不会触发退出状态。举个例子,执行注册的pointerdown输入时,你可以运行一个回调方法,稍后(比如250毫秒)将按钮重置到“out”帧。希望这个方法可以为你的按钮提供很好的参考。

===========================================================================Part3

今天官方放出了Phaser3,尽管只是一个beta版本。请继续关注我用Phaser3Beta20制作我的第一个Phaser3游戏。

虽然我现在提及的一些东西以后可能会被修复或者改变,但我会尽量为你们讲解一些以后不太可能改变的知识点。

概述:

1、没有补间的Preload

2、Graphics对象的特性

3、重新开始同一场景4、播放音频

1、没有补间的Preload

这个第一段讲述的是我的发现,而不是教程。

我想为我的游戏创建一个自定义的加载动画,而不是使用从左到右加载的标准进度条。我想在游戏加载时慢慢地把卡片翻转过来。

补间一开始运行正常,可是在加载资源时出错了。这是因为补间动画只能在场景的preload方法执行完毕才能运行。

问题是,所有的资源都是在preload方法中加载的,而preload方法在create方法之前执行。所以当执行create方法时,再运行加载动画就没有意义了。即使我在preload方法中创建补间动画,也得等到场景执行create方法时才能触发动画。手动迫使补间动画在preload方法中执行也失败了。

所以我就在想,在执行preload方法期间,进度条的宽度是如何变化的呢?因为它不是一个补间动画!实际上,当图像、音频、字体这些文件加载时,每次加载后的回调函数都会触发进度条图像的宽度改变。

this.load.on('progress', function(value)
{txt_percent.text = Math.round(value * 100) + ' %';
});

这意味着,只要能调用进度回调函数,就能创建自定义加载动画。

Phaser的加载进度表示为0到1之间的浮点数,再把它乘以100,就可以表示当前的加载进度。因此,如果你能想到一个不同的动画,并且把它跟数值联系起来,就能做出自定义加载动画。就像进度条图像一样,只需要数值参数就可以改变它的displayWidth属性。

但是补间动画只能在所有资源加载完成后才能运行。

2、Graphic对象的特性

我对Phaser2的Graphic对象太熟悉了,以至于我发现Phaser3的Graphic对象是如此惊人的不同。

2.1 X、Y坐标

让我们从创建一个graphic对象开始。我的大多数graphic对象都是UI背景或类似的东西。

我习惯在x,y位置创建graphic,而不是像许多例子一样在0, 0。

我的理由是:如果你在0,0创建了一个graphic对象,并在稍后把它拖拽到x,y,但是对象的实际位置依然在0, 0。如果你想在graphic对象中添加一个文本,并且这个文本的位置是参考graphic对象的,那么这个文本的坐标设为0,0是无效的。

对于整个canvas的原点来说也是一样。当你在Phaser3中创建一个graphic对象时,就再也不能给它传递x和y作为单独的参数了。你必须传递给对象一个x和y的属性。如果你像这样创建graphic对象,那么x和y属性将与对象的位置一致:

// We want our rectangle to be at 25, 100.
var x  = 25;
var y  = 100;
var bg = this.add.graphics({ x: x, y: y });// We make it solid black
bg.beginFill(0x000000, 1);// When you draw the rectangle, you have to start drawing at 0,0 now because
// this position is relative to what you have set in the create method above
var width  = 350;
var height = 200;
bg.drawRect(0, 0, width, height);// We are done!
bg.endFill();

2.2 Graphic的宽度和高度

现在你可以在graphic对象的相对坐标系创建对象了。

不幸的是,we cannot base anything off the graphic’s width and height properties anymore.(这句话实在是没看懂,-_-|)因为某些原因,Phaser3Beta20的graphic对象没有宽度和高度值。因此,如果你需要相对定位,你必须自己设置。

但是我会小心地设置属性名称,以防止跟以后发布的新版本Phaser属性名冲突。我不会使用bg.width,像bg.my_width这样类似的名称就很安全,不会跟未来可能会有的属性发生冲突。

// I wouldn't set these properties
bg.width = 350;// This is probably never going to be needed by the Phaser 3 framework
bg.my_width = 350;

2.3 Graphic的补间和alpha

最后一个问题是,对于一个已经存在的graohic对象,你无法为它创建补间,或者改变它的alpha。我不知这是否是一个bug,又或者是在Beta20版本中遗漏了。但是如果它很快加入了,那就太棒了。

如果你想改变graphic对象的alpha,你只能先把它清空–重置fillStyle–重绘。

// We create the graphics object
var bg = this.add.graphics({ x: 25, y: 100 });
bg.fillStyle(0x000000, 1);
bg.drawRect(0, 0, 350, 200);
bg.endFill();// Neither of these will work
bg.setAlpha(0.5);
bg.alpha = 0.5;// You have to clear & re-draw
bg.clear();
bg.fillStyle(0x000000, 0.5);
bg.drawRect(0, 0, 350, 200);
bg.endFill();

这使得它不适合补间动画。也许一个可能的解决办法就是做一个包含数次循环的补间,然后每次使用onRepeat回调函数来手动清除和重绘。

3、重新开始同一场景

在Phaser2中你可以从一个场景内部启动这个场景本身,也就是场景调用自身的init方法重启。这听起来可能有些乱,但实际上就是这样:重启场景scene(从Phaser2的state变成Phaser3的scene)。

Phaser3中你是无法在当前场景进行‘重启’的。

// If you are currently in the PlayScene, this will not work
this.scene.start('Play');

所以我想出一个办法:创建一个像这样的重定向场景:

var RedirectScene = new Phaser.Scene('Redirect');RedirectScene.init = function()
{'use strict';// ...
};RedirectScene.create = function()
{'use strict';this.bg = this.add.image(0, 0, 'bg-main').setOrigin(0);this.scene.start('Play');
};

如果场景之间可以传值我们就可以很容易地传递场景的name属性,并让RedirectScene变得更加灵活。如果你想在RedirectScene场景中重启不同的场景,首先你必须设置一个全局变量。

幸运的是,在我的游戏中,我只需要重启PlayScene场景。因此,我只要在RedirectScene中start(‘Play’)就行了。

4、播放音频

在教程临近尾声之时,我想说,在Phaser3中播放音频的功能也很棒。一旦音频文件加载到游戏中,你就可以随时播放它们。不用像在Phaser2中那样先创建一个sound对象了。这虽然是一个小变化,却使得添加音频变得更有乐趣。

// PreloadScene
this.load.audio('level_won', [ 'levelwin.ogg', 'levelwin.m4a' ]);// PlayScene
this.sound.play('level_won');

Phaser3初体验相关推荐

  1. 苹果电脑安装python3密码_mac系统安装Python3初体验

    前沿 对于iOS开发不要随便拆卸系统自带的Python,因为有很多 library 还是使用 Python2.7. 1 安装Xcode 1.1 App Store 搜索Xcode 并安装 1.2 安装 ...

  2. MapReduce编程初体验

    需求:在给定的文本文件中统计输出每一个单词出现的总次数 第一步: 准备一个aaa.txt文本文档 第二步: 在文本文档中随便写入一些测试数据,这里我写入的是 hello,world,hadoop he ...

  3. 小程序 缩放_缩放流星应用程序的初体验

    小程序 缩放 by Elie Steinbock 埃莉·斯坦博克(Elie Steinbock) 缩放流星应用程序的初体验 (First Experiences Scaling a Meteor Ap ...

  4. wxWidgets刚開始学习的人导引(3)——wxWidgets应用程序初体验

    wxWidgets刚開始学习的人导引全文件夹   PDF版及附件下载 1 前言 2 下载.安装wxWidgets 3 wxWidgets应用程序初体验 4 wxWidgets学习资料及利用方法指导 5 ...

  5. 用鸿蒙跑了个 “hello world”!鸿蒙开发初体验

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源 | https://my.oschina.net/u ...

  6. Windows Embedded Standard开发初体验(二)

    支持Silverlight的Windows Embedded Standard 好了,完成安装之后,我们就可以来做Windows Embedded Standard的第一个操作系统镜像了.在开始菜单中 ...

  7. 深度探索Hyperledger技术与应用之超级账本初体验(附部署代码)

    2019独角兽企业重金招聘Python工程师标准>>> 本章零基础地介绍了如何快速体验超级账本搭建的区块链网络,我们先绕过了比较复杂的初始化配置,用官方提供的fabric-sampl ...

  8. Spring环境搭建,IoC容器初体验~

    由于最近的任务是关于IoC配置文件格式的转换,所以需要从Spring的IoC容器开始学起,今天根据网上的介绍搭建了Spring环境,并对其IoC容器进行了初体验.文章中涉及到的软件以及推荐的一本关于S ...

  9. 来自新手Banana Pi香蕉派初体验

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 一段时间来对有强大的技术支持和完善的社区的Raspberry Pi很感兴趣,本想入一片学习学习,但转念一想Raspb ...

  10. 《深入理解Spark:核心思想与源码分析》——1.2节Spark初体验

    本节书摘来自华章社区<深入理解Spark:核心思想与源码分析>一书中的第1章,第1.2节Spark初体验,作者耿嘉安,更多章节内容可以访问云栖社区"华章社区"公众号查看 ...

最新文章

  1. java 项目使用 ajaxfileupload
  2. 干净卸载mysql (注册表)
  3. HTTP-GET, HTTP-POST and SOAP的比较
  4. 【OpenCV3】级联分类器训练——traincascade快速使用详解
  5. Android 极广推送接入
  6. linux 清空nat,linux 命令iptables -t nat
  7. CodeForces - 76E Points
  8. cmd静默运行_exe、msi、dos、bat等静默运行,后台运行,不弹窗的解决办法
  9. iPhone开发--在iPad上调用UIAlertController函数时发生crash
  10. 怎么用电脑录音,在电脑上录制音频的方法
  11. 初窥 Python 的 import 机制
  12. 大神论坛 利用活跃变量分析来去掉vmp的大部分垃圾指令
  13. 如何在win10下安装Docker
  14. 【Autojs教程】03-Autojs 控件学习 | 淘宝关注店铺取消实战
  15. (转)同居男女同一天的日记对比
  16. 高频交易(二)浅谈高频交易中比较成熟的一些交易策略
  17. 云服务器怎么安装虚拟主机,服务器上怎么安装虚拟主机
  18. 想设计一个母亲节小程序c语言,用小程序做一张精美海报,对母亲说一句:祝您母亲节快乐!...
  19. 【cdq分治】cdq分治与整体二分学习笔记Part2.cdq分治
  20. 人工智能学习总结(1)——人工智能的三个分支:认知、机器学习、深度学习

热门文章

  1. k8s教程(pod篇)-容器获取pod信息(Downward API)
  2. 浏览器主页被篡改修复
  3. PowerPoint安装IguanaTex方法
  4. ubuntu 19.04源
  5. CentOS7的locale配置
  6. JavaScript删除节点
  7. (转)50本书总结的50句话
  8. 服务架构演变~超详细
  9. win7安不了python_win7下安装python失败问题
  10. jpgraph 折线图--解决中文乱码的问题(标题和图例)