Simple Games Using SpriteKit
在ios7,苹果引入了SpriteKit,一个高性能渲染2D的框架。不像中心库(专注于画图)或中心动画(专注于动画过度),SpriteKit专注于不同领域-video games,它是苹果首次涉足ios的图形游戏编程的时代。在发布ios7盒OS X10.9(Mavericks. 2013年WWDC发布)的同时,为了写程序更为简单提供了相同的API在两个平台,尽管苹果从未像SpriteKit提供了一个框架,它有明显的相似之处是Cocos2D等各种开源库。如果你使用的是Cocos2D或类似的过去,你会感觉很熟悉。
现在创建一个工程并选择Game template名为:TextShooter
(sks文件只是标准的归档文件,你可以用NSKeyedUnarchiver和NSKeyedArchiver类来写和读)
)
xcode会为你初始化一些方法例如:
override func viewDidLoad() {
super.viewDidLoad() if let view = self.view as! SKView? {
// 初始化'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
// 让缩放比例填充整个窗口Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill // 加载这个场景(新场景取代旧场景)
view.presentScene(scene)
}
//当运行时,忽视父子类的关系
view.ignoresSiblingOrder = true
//在右下角显示FPS的值
view.showsFPS = true
//在右下角显示结点(node)的个数
view.showsNodeCount = true
}
}
了解完xcode自动初始化的代码,接下来我们自己手动初始化我们自己想要的,选择GameScence.swift,我们不需要didMoveToView()这个方法,现在改成:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)//获取当前位置
}
}
xcode自带一个GameScene.sks,里面没有我们想要的属性,所以得自己创建,需要添加属性为当前游戏等级数,生活玩家的数量,一个标志,让我们知道等级是否完成,修改GameScene.swift: private var levelNumber: Int //等级制度 private var playerLives: Int //玩家血 private var finished = false //当前游戏是否结束 class func scene(size:CGSize, levelNumber:Int) -> GameScene { return GameScene(size: size, levelNumber: levelNumber)}
override convenience init(size:CGSize) {
self.init(size: size, levelNumber: )
}
/*
创建名为SKLabelNode类的两个实例,并选择一个字体,设置一个文本值,指定一些对齐
*/
init(size:CGSize, levelNumber:Int) {
self.levelNumber = levelNumber
self.playerLives =
super.init(size: size)
backgroundColor = SKColor.lightGray()
let lives = SKLabelNode(fontNamed: "Courier")//指定字体
lives.fontSize =
lives.fontColor = SKColor.black()
lives.name = "LivesLabel"
lives.text = "Lives: \(playerLives)"
lives.verticalAlignmentMode = .top
lives.horizontalAlignmentMode = .right
lives.position = CGPoint(x: frame.size.width,
y: frame.size.height)
addChild(lives)
let level = SKLabelNode(fontNamed: "Courier")
level.fontSize =
level.fontColor = SKColor.black()
level.name = "LevelLabel"
level.text = "Level \(levelNumber)"
level.verticalAlignmentMode = .top
level.horizontalAlignmentMode = .left
level.position = CGPoint(x: , y: frame.height)
addChild(level)
}
required init?(coder aDecoder: NSCoder) {
levelNumber = aDecoder.decodeInteger(forKey: "level")
playerLives = aDecoder.decodeInteger(forKey: "playerLives")
super.init(coder: aDecoder)
}
/*
required的使用规则:required
修饰符只能用于修饰类初始化方法
当子类含有异于父类的初始化方法时(初始化方法参数类型和数量异于父类),子类必须要实现父类的required
初始化方法,并且也要使用required
修饰符而不是override
当子类没有初始化方法时,可以不用实现父类的required
初始化方法
*/
override func encode(with aCoder: NSCoder) {
aCoder.encode(Int(levelNumber), forKey: "level")
aCoder.encode(playerLives, forKey: "playerLives")
}
/*
我们给每个label命名,是因为init(coder:)和encode(with aCoder:)方法需要,所有SpriteKit结点,包括SKScene都遵循NSCoding协议
*/
我们配置了两个SKLabelNode,是时候让它们现身了,选择GameView.swift并添加以下代码:
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size: view.frame.size, levelNumber: 1) //configure the view
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true //Sprite Kit applies additional optimizations to improve rendering performance
skView.ignoresSiblingOrder = true //Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill skView.presentScene(scene)
}
现在你可以顺便了解下override var prefersStatusBarHidden,return true就是状态栏隐藏,反之。现在可以运行下,正常的结果:
背景有了,接下来可以添加一些互动了,毕竟是游戏,我们先添加一个发射子弹的头部,创建Cocoa Touch class 并以SKNode为父类,命名为playerNode,添加一下代码:
import SpriteKit class PlayerNode: SKNode {
override init() {
super.init()
name = "Player \(self)"
initNodeGraph() //初始化一个结点,内容为"^"(将V旋转180度)作为发射子弹的头部 }
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func initNodeGraph() {
let label = SKLabelNode(fontNamed: "Courier") //指定字体
label.fontColor = SKColor.blue
label.fontSize =
label.text = "v"
label.zRotation = CGFloat(Double.pi) //绕z轴旋转180度
label.name = "label"
self.addChild(label)
}
}
跟刚才添加level,lives一样,在GameScene,swift里实例化(实现)playerNode:
在addChild(level)后面加上这两行:
playerNode.position = CGPoint(x: frame.midX, y: frame.height * 0.1)
addChild(playerNode)
现在运行你就会看到如下场景:
现在我们来讨论如何用手指来移动它,这边插个题外话,在web前端里面坐标轴是已左上角为基准,但在SpriteKit我测试了下,添加一个结点并设置position(x:0,y:0),效果如下图所示:
所以这里的坐标是以左下角为基准。
我们假设当手指在屏幕下方的0.2部分(以下)滑动的时候就是有意要让发射器移动,下面这段代码就是这个意思:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if location.y < frame.height * 0.2 {
let target = CGPoint(x: location.x, y: playerNode.position.y)
playerNode.moveToward(target) //移动到手指当前位置
}
}
}
并且在playerNode类添加:
func moveToward(_ location: CGPoint) {
removeAction(forKey: "movement")
let distance = pointDistance(position, location) //计算当前位置和发射器位置的直线距离
let screenWidth = UIScreen.main.bounds.size.width
let duration = TimeInterval( * distance/screenWidth) //转换为时间间隔专用的单位,例如:毫秒
run(SKAction.move(to: location, duration: duration),
withKey:"movement")//duration:指定移动过程需要的时间,可以自己指定
}
可以发现上面的pointDistance()并没有定义,这边可以创建一个swift文件专门放置计算点或向量之类的算法,我的算法代码如下:
import UIKit // Takes a CGVector and a CGFLoat.
// 返回一个新向量(旧向量的x,y分量乘以参数CGPoint)
func vectorMultiply(_ v: CGVector, _ m: CGFloat) -> CGVector {
return CGVector(dx: v.dx * m, dy: v.dy * m)
}
// Takes two CGPoints.
// Returns a CGVector representing a direction from p1 to p2. func vectorBetweenPoints(_ p1: CGPoint, _ p2: CGPoint) -> CGVector {
return CGVector(dx: p2.x - p1.x, dy: p2.y - p1.y)
}
// Takes a CGVector.
// Returns a CGFloat containing the length of the vector, calculated using
// Pythagoras' theorem.
//√(x^2+y^2)
func vectorLength(_ v: CGVector) -> CGFloat {
return CGFloat(sqrtf(powf(Float(v.dx), ) + powf(Float(v.dy), )))
}
// Takes two CGPoints. Returns a CGFloat containing the distance between them,
// calculated with Pythagoras' theorem.
//√(x^2+y^2)
func pointDistance(_ p1: CGPoint, _ p2: CGPoint) -> CGFloat {
return CGFloat(
sqrtf(powf(Float(p2.x - p1.x), ) + powf(Float(p2.y - p1.y), )))
}
现在运行可以用手指轻触屏幕下方可以移动发射器了(当然在屏幕下方的0.2部分),并且移动速度也还不错,但在移动的过程中这个发射器什么都不会做,我们可以给它添加一些动作,翻转什么的:
func moveToward(_ location: CGPoint)
{
removeAction(forKey: "movement") let distance = pointDistance(position, location)
let screenWidth = UIScreen.main.bounds.size.width
let duration = TimeInterval( * distance/screenWidth) //转换为时间间隔专用的单位,例如:毫秒
run(SKAction.move(to: location, duration: duration),
withKey:"movement") //duration:指定移动过程需要的时间,可以自己指定 let wobbleTime = 0.3
let halfWobbleTime = wobbleTime/
let wobbling = SKAction.sequence([
SKAction.scaleX(to: 0.2, duration: halfWobbleTime),
SKAction.scaleX(to: 1.0, duration: halfWobbleTime)
])//接收一个action队列(数组)
let wobbleCount = Int(duration/wobbleTime)
//当duration大于wobbleTime时才会大于1,才会执行,所以当距离比较近的时候是不会旋转的,这个可以自由发挥
run(SKAction.repeat(wobbling, count: wobbleCount), withKey: "wobbling") }
现在运行的效果就比较好看一点,现在改添加一些敌人了,创建一个父类为SKNode,命名为:EnemyNode,并添加以下代码:
import SpriteKit class EnemyNode: SKNode {
override init() {
super.init()
name = "Enemy \(self)"
initNodeGraph()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func initNodeGraph() {
let topRow = SKLabelNode(fontNamed: "Courier-Bold")
topRow.fontColor = SKColor.brown
topRow.fontSize =
topRow.text = "x x"
topRow.position = CGPoint(x: -20, y: )//为了不显示在屏幕所以设定-20
addChild(topRow) let middleRow = SKLabelNode(fontNamed: "Courier-Bold")
middleRow.fontColor = SKColor.brown
middleRow.fontSize = 20
middleRow.position = CGPoint(x: -20, y: 0)
middleRow.text = "x"
addChild(middleRow) let bottomRow = SKLabelNode(fontNamed: "Courier-Bold")
bottomRow.fontColor = SKColor.brown
bottomRow.fontSize =
bottomRow.text = "x x"
bottomRow.position = CGPoint(x: -20, y: -)
addChild(bottomRow)
//三个SKLabelNode构成一个敌人
}
}
跟刚才一样在GameScene.swift加载该结点,并为这个结点设置一个函数随机生成x,y坐标来来生成敌人:
在addChild(playerNode)后面添加
spawnEnemies()//随机生成敌人
addChild(enemies)
private func spawnEnemies() {
let count = Int(log(Float(levelNumber))) + levelNumber
for _ in ..<count {
let enemy = EnemyNode()
let size = frame.size;
let x = arc4random_uniform(UInt32(size.width * 0.8))
+ UInt32(size.width * 0.1) //随机生成x坐标,范围0.1屏幕宽度~0.8屏幕宽度
let y = arc4random_uniform(UInt32(size.height * 0.5))
+ UInt32(size.height * 0.5) //随机生成y坐标,范围0.5屏幕高度~0.5屏幕高度
enemy.position = CGPoint(x: CGFloat(x), y: CGFloat(y))
enemies.addChild(enemy)
}
}
发射器有了,敌人也有了,现在该弄子弹了,创建一个BulletNode继承于SKNode:
//
// BulletNode.swift
// otherGame
//
// Created by 陈金伙 on 2017/4/8.
// Copyright © 2017年 cjh. All rights reserved.
// import SpriteKit
class BulletNode: SKNode {
var thrust:CGVector = CGVector(dx: , dy: ) override init() {
super.init()
let dot = SKLabelNode(fontNamed: "Courier")
dot.fontColor = SKColor.black
dot.fontSize =
dot.text = "."
addChild(dot)
let body = SKPhysicsBody(circleOfRadius: )
body.isDynamic = true
body.categoryBitMask = PlayerMissileCategory //用于定义物理主体所属的类别
body.contactTestBitMask = EnemyCategory //一个掩码,定义哪些类别的物体引起与这个物理体的交集通
body.collisionBitMask = EnemyCategory //定义哪些类别的物理机构可以与这个物理体碰撞
body.fieldBitMask = GravityFieldCategory //定义哪些类别的物理领域可以施加力量在这个物理机构
body.mass = 0.01 //以千克为单位的物体
physicsBody = body
name = "Bullet \(self)" } required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let dx = aDecoder.decodeFloat(forKey: "thrustX")
let dy = aDecoder.decodeFloat(forKey: "thrustY")
thrust = CGVector(dx: CGFloat(dx), dy: CGFloat(dy))
} override func encode(with aCoder: NSCoder) {
super.encode(with: aCoder)
aCoder.encode(Float(thrust.dx), forKey: "thrustX")
aCoder.encode(Float(thrust.dy), forKey: "thrustY")
} class func bullet(from start: CGPoint, toward destination: CGPoint) -> BulletNode {
let bullet = BulletNode()
bullet.position = start
let movement = vectorBetweenPoints(start, destination) //差的向量
let magnitude = vectorLength(movement) //两点之间的距离
let scaledMovement = vectorMultiply(movement, /magnitude) //缩放向量
let thrustMagnitude = CGFloat(100.0)
bullet.thrust = vectorMultiply(scaledMovement, thrustMagnitude)//扩大向量,无论屏幕多大都能发射到
bullet.run(SKAction.playSoundFileNamed("shoot.wav",
waitForCompletion: false))
return bulle
} func applyRecurringForce() {
physicsBody!.applyForce(thrust) //对物理体的重心施加力量,如果没有这个函数,发射的子弹会向下滑
} }
同理的向GameScene.swift添加
private let playerBullets = SKNode()
addChild(playerbullets)
并在
touchesBegan()中添加else(默认不移动发射器就是发射子弹)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if location.y < frame.height * 0.2 {
let target = CGPoint(x: location.x, y: playerNode.position.y)
playerNode.moveToward(target) //移动到手指当前位置
}
else { //没有移动发射器就默认发射子弹
let bullet = BulletNode.bullet(from: playerNode.position, toward: location)//从发射器当前位置发射到手指的位置
playerBullets.addChild(bullet)
}
}
}
现在我们考虑当子弹飞出屏幕时可以让它消失(从内存中撤销)在GameScene的update()添加:
override func update(_ currentTime: TimeInterval) {
updatebullets()
}
private func updatebullets() {
var bulletsToRemove:[BulletNode] = []
for bullet in playerBullets.children as! [BulletNode] { if !frame.contains(bullet.position) {
// 当子弹离开屏幕时放入一个数组
bulletsToRemove.append(bullet)
continue
}
// 对物理体的重心施加力量
bullet.applyRecurringForce()
}
playerBullets.removeChildren(in: bulletsToRemove)
}
现在的子弹遇到敌人并没有攻击性,因为我们只给BulletNode指定物理性质,现在该轮到PlayerNode,EnemyNode了,选择EnemyNode,并添加以下代码:
private func initPhysicsBody() {
let body = SKPhysicsBody(rectangleOf: CGSize(width: , height: ))
body.affectedByGravity = false
body.categoryBitMask = EnemyCategory
body.contactTestBitMask = PlayerCategory | EnemyCategory //发射器和子弹可以与之碰撞
body.mass = 0.2 //本身重量
body.angularDamping = //阻力
body.linearDamping =
body.fieldBitMask =
physicsBody = body
}
并在init()里添加刚才我们加的initPhysicsBody(),同理,在PlayerNode添加以下代码:
private func initPhysicsBody() {
let body = SKPhysicsBody(rectangleOf: CGSize(width: , height: ))
body.affectedByGravity = false
body.categoryBitMask = PlayerCategory
body.contactTestBitMask = EnemyCategory
body.collisionBitMask =
body.fieldBitMask =
physicsBody = body
}
并在init()添加initPhysicsBody(),现在可以运行试试看效果,当你把屏幕唯一的敌人打掉之后,你就应该想到下一步该设定升级了,当我们把敌人打出屏幕时,也应该像子弹那样从内存中移除,在GameScene添加以下代码:
private func updateEnemies() {
var enemiesToRemove:[EnemyNode] = []
for node in enemies.children as! [EnemyNode] {
if !frame.contains(node.position) {
enemiesToRemove.append(node)
continue
}
}
enemies.removeChildren(in: enemiesToRemove)
}
并更新update()函数的内容:
override func update(_ currentTime: TimeInterval) {
if finished {
return
}
updatebullets()
updateEnemies()
checkForNextlevel()
}
private func checkForNextlevel() { //查看是否还有敌人存活
if enemies.children.isEmpty {
goToNextLevel()
}
} private func goToNextLevel() { //进入下一级
finished = true let label = SKLabelNode(fontNamed: "Courier")
label.text = "Level Complete!"
label.fontColor = SKColor.blue
label.fontSize =
label.position = CGPoint(x: frame.size.width * 0.5, y: frame.size.height * 0.5)
addChild(label) let nextLevel = GameScene(size: frame.size, levelNumber: levelNumber + ) //等级不断增加
nextLevel.playerLives = playerLives //生命值不变
view!.presentScene(nextLevel, transition: SKTransition.flipHorizontal(withDuration: 1.0))
}
这个游戏里的每个结点都是模拟现实物理的,所以我们还要考虑当我们用子弹打到第一个敌人时,敌人会被打飞,被打飞的过程中或许会碰撞到另一个敌人又或许会随重力下滑,掉落到发射器上,即玩家生命值减一操作,这就需要委托了,因为委托事件里面有contact事件对当前阶段很好用,添加
class GameScene: SKScene, SKPhysicsContactDelegate {
physicsWorld.gravity = CGVector(dx: 0, dy: -1) //设置重力向下
physicsWorld.contactDelegate = self
这边我们可以先想想碰撞的特效xcode提供自带的文件,创建SpriteKit partical file命名MissleExplosion,并在inspector属性进行调整,这边是我的(随便调的,自由发挥):
同理再创建一个命名为EnemyExplosion,并在inspector属性进行调整(自由发挥)
选择GameScene添加以下代码:
func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == contact.bodyB.categoryBitMask {
//一样的种类
let nodeA = contact.bodyA.node!
let nodeB = contact.bodyB.node!
}
else {
var attacker: SKNode
var attackee: SKNode if contact.bodyA.categoryBitMask > contact.bodyB.categoryBitMask {//种类的大小,下面有给图说明
// A attack B
attacker = contact.bodyA.node!
attackee = contact.bodyB.node! }
else {
//B attack A
attacker = contact.bodyB.node!
attackee = contact.bodyA.node!
}
if attackee is PlayerNode {
playerLives -=
} //What do we do with the attacker and the attackee?
attackee.receiveAttacker(attacker, contact: contact)//扩展类的方法,下面有给
playerBullets.removeChildren(in: [attacker])
enemies.removeChildren(in: [attacker]) }
}
四种大小分别代表不同的种类,在我们给他们的physicBody初始化时就有给他们指定,接下来扩展SKnode类,为什么要扩展SKNode?,因为在SpriteKit每个对象都是一个结点,所以扩展SKNode,可以对敌人,发射器,子弹都好操作,新建一个swift file命名SKNode+Extra并添加以下代码:
import SpriteKit extension SKNode {
func receiveAttacker(_ attacker: SKNode, contact: SKPhysicsContact)
{
// Default implementation does nothing physicsBody!.affectedByGravity = true
let force = vectorMultiply(attacker.physicsBody!.velocity, contact.collisionImpulse) let myContact = scene!.convert(contact.contactPoint, to: self)
physicsBody!.applyForce(force, at: myContact) let path = Bundle.main.path(forResource: "MissileExplosion", ofType: "sks") let explosion = NSKeyedUnarchiver.unarchiveObject(withFile: path!)
as! SKEmitterNode
explosion.numParticlesToEmit = //默认为0,无限粒子,这边指定20颗粒子
explosion.position = contact.contactPoint //在子弹击中的部位出现粒子
scene!.addChild(explosion)
} func friendlyBumpFrom(_ node: SKNode) {
// Default implementation does nothing
physicsBody!.affectedByGravity = true }
}
现在运行你会发现一切都良好,就是尽管敌人掉落到发射器上,玩家的血是没有扣的,我记得明明有添加
if attackee is PlayerNode {
playerLives -= 1
}
可是不起作用,其实是有起作用的,不信你可以调试下在后台输出playerLives,只是没有实时更新到界面,
private var playerLives: Int {
didSet {
let lives = childNode(withName: "LivesLabel") as! SKLabelNode
lives.text = "Lives: \(playerLives)"
}
}
更改私有属性变成属性观察者,一旦playerlives有变化就执行didSet里面的代码,现在可以了,但是生命值会一直减,没有尽头的,就像是无敌模式,是时候给这个游戏来个收尾了,
创建cocoa touch class命名GameOverScene,并以SKScene为父类,添加以下代码:
import SpriteKit class GameOverScene: SKScene {
override init(size: CGSize) {
super.init(size: size)
backgroundColor = SKColor.purple
let text = SKLabelNode(fontNamed: "Courier")
text.text = "Game Over"
text.fontColor = SKColor.white
text.fontSize =
text.position = CGPoint(x: frame.size.width/, y: frame.size.height/)
addChild(text)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
这就是结束界面,在GameScene里面来实现它:
private func triggerGameOve() {
finished = true let path = Bundle.main.path(forResource:"EnemyExplosion",
ofType: "sks")
let explosion = NSKeyedUnarchiver.unarchiveObject(withFile: path!)
as! SKEmitterNode
explosion.numParticlesToEmit = //当生命值为0时,爆炸变的更大
explosion.position = playerNode.position
scene!.addChild(explosion)
playerNode.removeFromParent() let transition = SKTransition.doorsOpenVertical(withDuration: )
let gameOver = GameOverScene(size: frame.size)
view!.presentScene(gameOver, transition: transition)
} private func checkForGame() -> Bool { //添加到update(),实时监测
if playerLives == {
triggerGameOve()
return true
}
return false
} override func update(_ currentTime: TimeInterval) {
if finished {
return
}
updatebullets()
updateEnemies()
if (!checkForGame()) {
checkForNextlevel()
} }
现在基本可以完了,要是想要美观的话可以搞个开始界面,这边就不搞了。
要下载全部的代码请到我的github库:https://github.com/TypeInfos/SpriteKit-Game
Simple Games Using SpriteKit的更多相关文章
- iPhone Tutorials
http://www.raywenderlich.com/tutorials This site contains a ton of fun written tutorials – so many t ...
- How I came to find Linux
http://ianmurdock.com/post/how-i-came-to-find-linux/ lan murdock August 17, 2015 I saw my first Sun ...
- Intro to Airplane Physics in Unity 3D – 2017 and 2018
Info:DescriptionHave you ever wanted to build your own Airplane Physics using the Rigidbody componen ...
- How do I learn machine learning?
https://www.quora.com/How-do-I-learn-machine-learning-1?redirected_qid=6578644 How Can I Learn X? ...
- 非常优秀的iphone学习文章总结!
This site contains a ton of fun tutorials – so many that they were becoming hard to find! So I put t ...
- Tetris
he Tetris game is one of the most popular computer games ever created. The original game was designe ...
- [转]Using OData from ASP.NET
本文转自:http://www.drdobbs.com/windows/using-odata-from-aspnet/240168672 By Gastón Hillar, July 01, 201 ...
- Exercises for IN1900
Exercises for IN1900October 14, 2019PrefaceThis document contains a number of programming exercises ...
- 【英语魔法俱乐部——读书笔记】 1 初级句型-简单句(Simple Sentences)
第一部分 1 初级句型-简单句(Simple Sentences):(1.1)基本句型&补语.(1.2)名词短语&冠词.(1.3)动词时态.(1.4)不定式短语.(1.5)动名词.(1 ...
随机推荐
- 一个可扩展的深度学习框架的Python实现(仿keras接口)
一个可扩展的深度学习框架的Python实现(仿keras接口) 动机 keras是一种非常优秀的深度学习框架,其具有较好的易用性,可扩展性.keras的接口设计非常优雅,使用起来非常方便.在这里,我将 ...
- servlet+jsp+jdbc实现从数据库查询用户信息到页面
工程创建这里就不在累述了,直接从显示User信息列表开始. 备注:我用的是servlet3的注解功能实现的,所以不需要配置web.xml 这是我的工程目录: 首先我们创建实体类: public cla ...
- iBatis & myBatis & Hibernate 要点记录
iBatis & myBatis & Hibernate 要点记录 这三个是当前常用三大持久层框架,对其各自要点简要记录,并对其异同点进行简单比较. 1. iBatis iBatis主 ...
- fragment显示 Binary XML file line #12: Error inflating class fragment 错误
问题 最近换了新机子,今天在静态用fragment时突然发现闪退,一看显示 Binary XML file line #12: Error inflating class fragment 错误 后面 ...
- Python学习笔记整理总结【web基础】【web/HTML/CSS/JavaScript/DOM/jQuery】
一.HTML HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记).相当于定义统一的一套规则,大家都来遵守他,这样就可以 ...
- Java基础概念1
一.Java数据类型 1.byte 字节型 1byte = 8bit 表示数范围:-2^7~2^7-1(-128~127): 2.short 短整型 2 byte = 16bit 表示数范围:-2^1 ...
- Numpy数组的基本运算操作
一.算术运算符 In [3]: a = np.arange(0,5) Out[3]array([0, 1, 2, 3, 4]) In [4]: a+4 Out[4]: array([4, 5, 6, ...
- weex 环境搭建
最近为了项目需要(实际上是为了年底KPI),领导要求用3天时间,学习并使用weex开发一个页面,说实话,压力山大.在这之前压根儿就没听说过啊,一脸懵逼 无奈之余只能Google了,惊喜的发现weex的 ...
- 实际应用中遇到TimedRotatingFileHandler不滚动的问题
需求: 程序每天晚上8点和10点定时运行,期望日志按日期记录 添加Handler部分代码如下: formatter = logging.Formatter("%(asctime)s %(fi ...
- 利用InfluxDB和Grafana搭建数据监测的仪表盘
InfluxDB的介绍及安装 InfluxDB是支持持续写入的时序数据库,常用于监测系统所需要的数据的存储. 官网的详细安装步骤 https://docs.influxdata.com/influxd ...