Screeps 抽象角色系統(tǒng)

screeps 系列教程

簡介

在設(shè)計自己的角色系統(tǒng)的時候炼绘,很多人都會被角色越來越多的問題所困擾,本文不討論如何去削減角色的數(shù)量杨何,而是從“發(fā)布成本”的角度出發(fā),來介紹如何解決這個問題沥邻,并提高角色系統(tǒng)的可維護(hù)性危虱。

本文將會使用到以下概念,如果對其不太了解唐全,可以先閱讀后方的拓展鏈接:

什么是發(fā)布成本埃跷?

在正式開始前,我們先來簡單了解一下什么是發(fā)布成本,發(fā)布成本可以簡單的理解成 創(chuàng)建一個新角色時要新增的代碼量弥雹。發(fā)布成本越高垃帅,我們就越抗拒在自己的系統(tǒng)里加入新的角色。那么反過來缅糟,假如我們創(chuàng)建一個新角色只需要寥寥十幾行甚至幾行代碼就可以完成挺智,非常簡單的就可以完成新角色的加入,那么不就從根本上 放棄治療 解決問題了么窗宦?

減少發(fā)布成本

如何降低發(fā)布成本赦颇?

降低發(fā)布成本的核心思想就是 將不同角色中的可復(fù)用代碼抽象出來,形成一個新的“平臺”赴涵,而把不可復(fù)用的邏輯代碼整合成統(tǒng)一的配置項媒怯。這樣,在發(fā)布新角色時我們只需復(fù)制配置項模板髓窜,然后填寫其中的可變邏輯即可扇苞。

你或許在游戲的過程中已經(jīng)或多或少的做過了類似的事情,例如將狀態(tài)的更新邏輯封裝成一個函數(shù)寄纵,或是將常用的 creep 方法封裝起來鳖敷。同樣的,本文的主要內(nèi)容就是如何高效的將不可變的邏輯抽象出來程拭,避免大家少走彎路定踱。

《設(shè)計模式》 —— GoF

考慮你的設(shè)計中哪些地方可能變化,這種方式與關(guān)注會導(dǎo)致重新設(shè)計的原因相反恃鞋。它不是考慮什么時候會迫使你的設(shè)計改變崖媚,而是考慮你怎樣才能夠 在不重新設(shè)計的情況下進(jìn)行改變。這里的關(guān)鍵在于封裝發(fā)生變化的概念恤浪,這是許多設(shè)計模式的主題畅哑。

了解 creep 的通用運行模式

在抽象可復(fù)用的代碼之前,我們要先了解 creep 的運行模式水由,通俗點說就是每個 creep 都要執(zhí)行的代碼邏輯荠呐。通過對基本的采集者harvester、升級者upgrader砂客、建筑者builder進(jìn)行觀察直秆,我們不難看出:

creep 運行時通常會在兩個狀態(tài)之間進(jìn)行循環(huán)。

例如鞭盟,我們對 Screeps 基本角色系統(tǒng) 一文中提到的角色進(jìn)行拆分:

英文名 角色名 狀態(tài)A 切換條件 狀態(tài)B
havester 采礦者 開采能量 carry 是否到達(dá)上限 存入指定的結(jié)構(gòu)
upgrader 升級者 取出能量 carry 是否到達(dá)上限 升級房間控制器
builder 建造者 取出能量 carry 是否到達(dá)上限 建造結(jié)構(gòu)
carrier 運輸者 取出能量 carry 是否到達(dá)上限 存入指定的結(jié)構(gòu)
repairer 維修者 取出能量 carry 是否到達(dá)上限 修復(fù)受損的結(jié)構(gòu)
defender 防御者 駐守指定區(qū)域 房間內(nèi)是否有入侵者 攻擊入侵者

但是這并不是全部,例如在開拓者 (在新殖民房間中建造 spawn) 需要先抵達(dá)執(zhí)行房間瑰剃,然后再執(zhí)行建造者的邏輯齿诉。又比如士兵,可能需要在作戰(zhàn)前先獲取強(qiáng)化。所以說:

在某些角色中粤剧,需要執(zhí)行一個額外的準(zhǔn)備階段歇竟。

所以,我們可以整理出如下 creep 生命周期:

creep 生命周期

進(jìn)而抵恋,我們就可以得到如下 creep 生命周期階段的基本結(jié)構(gòu)焕议,這一段只是用作說明,不需要加進(jìn)你的代碼弧关。使用了 typescript 中的接口來描述盅安,如果你沒有用過 typescript 的話,可以參考其中的注釋進(jìn)行理解

/**
 * creep 生命周期階段
 */
interface CreepLifeCycle {
    /**
     * [可選] 準(zhǔn)備階段世囊,接受 creep 并執(zhí)行對應(yīng)的準(zhǔn)備邏輯
     * 根據(jù)其返回值判斷是否準(zhǔn)備完成别瞭,在準(zhǔn)備完成前是不會執(zhí)行下面的 target 和 source 階段的
     */
    prepare?: (creep: Creep) => boolean
    /**
     * [必須] 工作階段,接受 creep 并執(zhí)行對應(yīng)的工作邏輯(例如建造建筑株憾,升級控制器)
     * 在返回 true 時代表所需資源不足蝙寨,將在紫萼個 tick 開始執(zhí)行 source 階段
     */
    target: (creep: Creep) => boolean
    /**
     * [可選] 資源獲取階段,接受 creep 并執(zhí)行對應(yīng)的資源獲取邏輯(例如獲取能量嗤瞎,采集礦物)
     * 在返回 true 時代表能量獲取完成墙歪,將在下個 tick 開始執(zhí)行 target 階段
     */
    source?: (creep: Creep) => boolean
}

注意,這里每個階段的值都是函數(shù)Function贝奇,我們對應(yīng)階段的實際代碼邏輯就包含在這些函數(shù)里虹菲,這樣對于底層架構(gòu)來說,只需要根據(jù) creep 當(dāng)前的狀態(tài)調(diào)用不同的函數(shù)即可弃秆,不需要關(guān)心 creep 的具體工作邏輯是怎樣的届惋。這在軟件設(shè)計上被稱為 關(guān)注點分離

設(shè)計 creep 運行流程

我們已經(jīng)了解了 creep 的運行模式菠赚,現(xiàn)在來重新設(shè)計一下代碼流程脑豹,使其可以兼容我們的新設(shè)計。

首先衡查,我們要將角色的邏輯整合在一起做成一個函數(shù)瘩欺,這個函數(shù)接受必要的參數(shù),并返回要執(zhí)行的工作邏輯拌牲,而返回的工作邏輯對象的結(jié)構(gòu)就是上文中的 CreepLifeCycle俱饿。為什么要這么設(shè)計呢?必要的參數(shù)又是什么呢塌忽?

要解答這個問題拍埠,我們先來思考一下在設(shè)計角色邏輯時面臨的最大問題是什么。是的土居,如何獲取自己的操作目標(biāo)枣购。這里的操作目標(biāo)指的是 creep 在工作時要面對的東西嬉探,例如 harvester 要采集的 source。你是不是糾結(jié)了很久如何讓 creep 采集不同的 source棉圈?

在之前的代碼中涩堤,我們的工作邏輯里耦合了太多由 if-else 組成的目標(biāo)獲取代碼,例如根據(jù)某個內(nèi)存字段獲取到不同的 source 對象分瘾。這實際上違反了 單一職責(zé)原則胎围。所以我們現(xiàn)在將這些目標(biāo)獲取代碼拿到外邊,然后通過函數(shù)參數(shù)的形式傳遞給 creep 的工作邏輯德召,工作邏輯不用關(guān)心這些目標(biāo)是怎么來的白魂,直接無腦執(zhí)行即可,這樣就保證了工作函數(shù)的純潔性氏捞。

這個參數(shù)根據(jù)角色的不同也是不一樣的碧聪,例如 harvester 會接受一個 source Id 作為他要采集的能量來源,而 defender 會接受一個房間名作為他要防御的目標(biāo)房間液茎。

接下來我們會在內(nèi)存中創(chuàng)建一個對象來保存這些配置逞姿,并開放一套 api 來對這個配置對象進(jìn)行管理。最后我們會在Creep原型上添加一個 work 方法捆等,將我們上一小節(jié)中的生命周期邏輯存放到其中滞造。這樣只需要遍歷 Game.creeps 并調(diào)用creep.work()即可完成每個 creep 的工作。

具體的流程如下:

代碼流程

實現(xiàn)運行流程

上一小節(jié)是不是看的有些暈栋烤,沒有關(guān)系谒养,接下來接下來我們會把所有的代碼實現(xiàn)出來并一一講解。為了方便理解明郭,這里會按照上面的流程圖 從下往上 進(jìn)行實現(xiàn)买窟。本節(jié)內(nèi)容推薦先在 訓(xùn)練場 中進(jìn)行實驗。

1> 在配置項中定義運行邏輯

首先我們實現(xiàn)最后一步:在配置項中定義運行邏輯薯定,按照上面的CreepLifeCycle實現(xiàn)最簡單的upgrader升級者始绍,新建role.upgrader.js并填入如下內(nèi)容:

/**
 * 升級者配置生成器
 * source: 從指定礦中挖礦
 * target: 將其轉(zhuǎn)移到指定的 roomController 中
 * 
 * @param sourceId 要挖的礦 id
 */
module.exports = sourceId => ({
    // 采集能量礦
    source: creep => {
        const source = Game.getObjectById(sourceId)
        if (creep.harvest(source) == ERR_NOT_IN_RANGE) creep.moveTo(source)

        // 自己身上的能量裝滿了,返回 true(切換至 target 階段)
        return creep.store.getFreeCapacity() <= 0
    },
    // 升級控制器
    target: creep => {
        const controller = creep.room.controller
        if (creep.upgradeController(controller) == ERR_NOT_IN_RANGE) creep.moveTo(controller)

        // 自己身上的能量沒有了话侄,返回 true(切換至 source 階段)
        return creep.store[RESOURCE_ENERGY] <= 0
    }
})

可以看到我們用非常少的代碼就實現(xiàn)了升級者的邏輯亏推。當(dāng)然這里并不能直接運行,稍后我們會繼續(xù)進(jìn)行完善年堆。

這里先簡單介紹一下這段代碼吞杭,可以看到最外層我們用module.exports和箭頭函數(shù)導(dǎo)出了一個函數(shù),這個函數(shù) 接收一個能量礦的 id 变丧,并返回升級者的工作邏輯芽狗,這里返回的工作邏輯對象就是上文中的 CreepLifeCycle。稍后我們會使用這個函數(shù)快捷的生成一個升級者痒蓬。而由于升級者不需要準(zhǔn)備階段童擎,所以我們省略了prepare階段的實現(xiàn)曼月。

值得注意的是 source 和 target 方法的返回值,最終的框架會根據(jù)其返回值決定是否要切換至另一個階段柔昼。

2> 創(chuàng)建 creep 管理 api

ok,接下來我們來創(chuàng)建一個全局模塊炎辨,這個模塊將負(fù)責(zé) creep 的增刪捕透。新增文件 creepApi.js 并填入如下內(nèi)容:

global.creepApi = {
    /**
     * 新增 creep 配置項
     * @param configName 配置項名稱
     * @param role 該 creep 的角色
     * @param args creep 的工作參數(shù)
     */
    add(configName, role, ...args) {
        if (!Memory.creepConfigs) Memory.creepConfigs = {}
        Memory.creepConfigs[configName] = { role, args }
        
        return `${configName} 配置項已更新:[角色] ${role} [工作參數(shù)] ${args}`
    },
    /**
     * 移除指定 creep 配置項
     * @param configName 要移除的配置項名稱
     */
    remove(configName) {
        delete Memory.creepConfigs[configName]
        return `${configName} 配置項已移除`
    },
    /**
     * 獲取 creep 配置項
     * @param configName 要獲取的配置項名稱
     * @returns 對應(yīng)的配置項,若不存在則返回 undefined
     */
    get(configName) {
        if (!Memory.creepConfigs) return undefined
        return Memory.creepConfigs[configName]
    }
}

這個模塊一共暴露了三個方法碴萧,分別用于添加 creep 配置乙嘀、移除配置以及獲取配置,非常的簡單破喻。注意其中使用了 es6 的 解構(gòu)操作符 ... 來讓代碼更加精簡虎谢。

好了,現(xiàn)在我們已經(jīng)有了配置工具曹质,接下來我們將拓展 Creep 原型婴噩,讓 creep 們可以從自己持有的配置中明白需要做什么。

4> 進(jìn)行 Creep 拓展

首先新建mount.creep.js羽德,并填入如下內(nèi)容:

/**
 * 引入 creep 配置項
 * 其鍵為角色名(role)几莽,其值為對應(yīng)角色的邏輯生成函數(shù)
 */
const roles = {
    upgrader: require('role.upgrader.js')
}

// 添加 work 方法
Creep.prototype.work = function() {

    // ------------------------ 第一步:獲取 creep 執(zhí)行邏輯 ------------------------

    // 獲取對應(yīng)配置項
    const creepConfig = creepApi.get(this.memory.configName)
    // 檢查 creep 內(nèi)存中的配置是否存在
    if (!creepConfig) {
        console.log(`creep ${this.name} 攜帶了一個無效的配置項 ${this.memory.configName}`)
        this.say('找不到配置!')
        return 
    }
    const creepLogic = roles[creepConfig.role](...creepConfig.args)

    // ------------------------ 第二步:執(zhí)行 creep 準(zhǔn)備階段 ------------------------

    // 沒準(zhǔn)備的時候就執(zhí)行準(zhǔn)備階段
    if (!this.memory.ready) {
        // 有準(zhǔn)備階段配置則執(zhí)行
        if (creepLogic.prepare) {
            this.memory.ready = creepLogic.prepare(this)
        }
        // 沒有就直接準(zhǔn)備完成
        else this.memory.ready = true
        return
    }

    // ------------------------ 第三步:執(zhí)行 creep 工作階段 ------------------------

    let stateChange = true
    // 執(zhí)行對應(yīng)階段
    // 階段執(zhí)行結(jié)果返回 true 就說明需要更換 working 狀態(tài)
    if (this.memory.working) {
        if (creepLogic.target) stateChange = creepLogic.target(this)
    }
    else {
        if (creepLogic.source) stateChange = creepLogic.source(this)
    }

    // 狀態(tài)變化了就切換工作階段
    if (stateChange) this.memory.working = !this.memory.working
}

這一段代碼比較長宅静,我們來詳細(xì)介紹一下章蚣,首先我們引入了 role.upgrader.js 并將其放在一個對象 roles 中,這個對象包含了我們所有的角色姨夹,后期我們新增了角色的話需要添加到這里纤垂。

然后我們通過修改 Creep 原型的方式為所有的 creep 都添加了 work 方法,這個方法中包含的內(nèi)容就是我們在一開始提到的 “基礎(chǔ)框架”磷账。其中一共包含了三部分峭沦,上面已經(jīng)通過注釋形式標(biāo)注了起來,分別是:

  • 獲取工作邏輯:通過 creep 內(nèi)存中保存的 configName 字段借助 creepApi 獲取對應(yīng)的配置項够颠。

  • 執(zhí)行準(zhǔn)備階段:檢查 creep 內(nèi)存中的 ready 字段熙侍,如果不為 true 的話則說明 creep 還沒準(zhǔn)備好,去執(zhí)行準(zhǔn)備階段履磨。在準(zhǔn)備完成前不會執(zhí)行下面的工作階段蛉抓。

  • 執(zhí)行工作階段:狀態(tài)機(jī),檢查 creep 內(nèi)存中的 working 字段剃诅,如果為 true 則執(zhí)行 target 階段巷送,為 false 就執(zhí)行 source 階段,并根據(jù)這兩個階段的返回值決定要不要切換狀態(tài)矛辕。

你可以通過下面這張圖理解 creep 是如何找到自己要執(zhí)行的代碼的:

5> 掛載拓展并調(diào)用 creep

ok笑跛,現(xiàn)在我們已經(jīng)完成了全部的準(zhǔn)備工作付魔,接下來只需要把他們實裝即可,在main.js里填寫如下代碼:

// 掛載 creep 管理模塊
require('creepApi.js')
// 掛載 creep 拓展
require('mount.creep.js')

module.exports.loop = function() {
    // 遍歷所有 creep 并執(zhí)行上文中拓展的 work 方法
    Object.values(Game.creeps).forEach(creep => creep.work())
}

現(xiàn)在我們就可以來進(jìn)行測試了飞蹂,首先執(zhí)行如下代碼來孵化一個 creep:

// 注意修改其中的 spawn 名稱
Game.spawns.Spawn1.spawnCreep([WORK, CARRY, MOVE], 'firstUpgrader', { memory: { configName: 'upgrader1' }})

有一點和官方教程不同的是几苍,在 creep 內(nèi)存中保存了 configName: upgrader1 而不是 role: upgrader,因為在這個架構(gòu)里陈哑,不同的升級者的配置是不同的(例如 upgrader1 會去能量礦 A妻坝,而 upgrader2 會去能量礦 B ),所以我們要通過upgrader1來找到其對應(yīng)的配置項惊窖。

在他孵化完成后你可以看到它在嚷嚷著找不到配置項刽宪,這是因為我們給他內(nèi)存中設(shè)置的配置 upgrader1 并不存在,接下來我們在控制臺執(zhí)行如下代碼來新建這個配置:

// 注意把第三個參數(shù)改成房間中存在的 source id
creepApi.add('upgrader1', 'upgrader', '5bbcaa7d9099fc012e631786')

現(xiàn)在我們就能看到 creep 已經(jīng)開始執(zhí)行他的升級任務(wù)了界酒!

NICE

這行代碼的意思就是新增配置項 upgrader1圣拄,指定角色為 upgrader,將采集對應(yīng) source 中的能量并升級 controller毁欣,是不是非常簡單庇谆。你可以將上面的 source id 換成房間內(nèi)的另一個 source,然后再執(zhí)行一遍署辉,然后就可以看到 creep 迅速的響應(yīng)了我們的變更族铆。

你也可以在控制臺執(zhí)行下面的代碼來刪除配置項,刪除后 creep 將會重新變?yōu)橐粋€無頭蒼蠅:

creepApi.remove('upgrader1')

也就是說哭尝,我們只需要使用 creepApi 對配置項進(jìn)行控制哥攘,就可以靈活的指導(dǎo) creep 的行為邏輯。而不用關(guān)系其他角色細(xì)節(jié)材鹦,即下圖所示:

和其他模塊進(jìn)行對接

這里為了簡單起見逝淹,我們手動創(chuàng)建了 creep 的配置項,如果你還是個新手的話桶唐,推薦你先以這種形式手動調(diào)整房間的運營單位來積累經(jīng)驗栅葡,在你對游戲的了解有所深入之后,你可以嘗試結(jié)合自己的 spawn 孵化模塊和 creep 數(shù)量控制模塊來動態(tài)的調(diào)用 creepApi 進(jìn)行 creep 增刪 以達(dá)到動態(tài)調(diào)整運營單位的目的尤泽,調(diào)用方式和上面控制臺命令完全一致欣簇,這里不再過多深入。

寫在最后

本文中提到的框架并不復(fù)雜坯约,只有兩個需要注意的點:

  • source 和 target 生命周期階段會根據(jù)函數(shù)的返回值(是否為 true)決定下個 tick 是否要切換為另一個階段熊咽。
  • creepApi 是指導(dǎo) creep 工作的核心工具。通過其他模塊調(diào)用 creepApi闹丐,可以完成各種各樣的 creep 工作横殴。

如果你對上面提到的代碼還有不了解的地方,推薦把上面的 設(shè)計 creep 運行流程 小節(jié)多讀幾次卿拴。接下來提幾點可以優(yōu)化的地方衫仑,你可以酌情考慮升級:

  • 添加 isNeed 階段:上面配置項只能滿足那些會一直生成的 creep 發(fā)布梨与,而元素礦采集單位和房間守衛(wèi)這種有可能很長時間都不會孵化的單位該怎么辦呢?通過添加額外的 isNeed 階段文狱,并在 spawn 孵化前進(jìn)行檢查粥鞋,這樣就可以決定是否要重新孵化某個單位。
  • 在配置項中添加 body 函數(shù):creep 在不同時期的體型是會發(fā)生改變的瞄崇,我們可以在配置中添加一個 body 函數(shù)陷虎,這個函數(shù)會在孵化時由 spawn 調(diào)用,并將函數(shù)的返回值作為要孵化 creep 的 body 體型杠袱,由此來提高角色的內(nèi)聚性。

如果你不知道如何著手進(jìn)行修改的話窝稿,可以參考我的 Screeps 項目 HoPGoldy/my-screeps-ai楣富。以上就是本文的全部內(nèi)容了,了解更多 Screeps 的中文教程伴榔?歡迎訪問 Screeps - 中文系列教程纹蝴!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市踪少,隨后出現(xiàn)的幾起案子塘安,更是在濱河造成了極大的恐慌,老刑警劉巖援奢,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兼犯,死亡現(xiàn)場離奇詭異,居然都是意外死亡集漾,警方通過查閱死者的電腦和手機(jī)切黔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來具篇,“玉大人纬霞,你說我怎么就攤上這事∏裕” “怎么了诗芜?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長埃疫。 經(jīng)常有香客問我伏恐,道長,這世上最難降的妖魔是什么熔恢? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任脐湾,我火速辦了婚禮,結(jié)果婚禮上叙淌,老公的妹妹穿的比我還像新娘秤掌。我一直安慰自己愁铺,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布闻鉴。 她就那樣靜靜地躺著茵乱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孟岛。 梳的紋絲不亂的頭發(fā)上瓶竭,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機(jī)與錄音渠羞,去河邊找鬼斤贰。 笑死,一個胖子當(dāng)著我的面吹牛次询,可吹牛的內(nèi)容都是我干的荧恍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼屯吊,長吁一口氣:“原來是場噩夢啊……” “哼送巡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起盒卸,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤骗爆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蔽介,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體摘投,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年虹蓄,在試婚紗的時候發(fā)現(xiàn)自己被綠了谷朝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡武花,死狀恐怖圆凰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情体箕,我是刑警寧澤专钉,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站累铅,受9級特大地震影響跃须,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜娃兽,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一菇民、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦第练、人聲如沸阔馋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽呕寝。三九已至,卻和暖如春婴梧,著一層夾襖步出監(jiān)牢的瞬間下梢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工塞蹭, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留孽江,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓番电,卻偏偏與公主長得像竟坛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子钧舌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容

  • feisky云計算、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 3,847評論 0 5
  • 前言 本文來討論一下 Screeps 中最重要的 角色系統(tǒng) 的設(shè)計方案以及一些基本原則涎跨。注意洼冻,本文中討論的內(nèi)容可能...
    HoPGoldy閱讀 13,596評論 3 12
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,098評論 1 32
  • 生活中我是一個能不做的事情就不做,覺得必要做的事情才去做隅很,所以我是一個特別怕事的人撞牢,那么對于像我這樣的懶人有什么辦...
    百凌愛讀書閱讀 921評論 0 1
  • 7:00起床 7:00-8:00洗漱、早餐叔营。 8:00-8:30半小時專業(yè)閱讀屋彪。 8:30-12:00上午的正式工...
    魏金寶閱讀 165評論 0 0