分类 编程.游戏开发 中的文章

抽卡逻辑自述

抽卡逻辑自述

参考《剑与远征》

卡牌颜色以及合成

颜色合成
绿色可分解,得到粉尘和银瓜子, 最基础的卡
蓝+同样的三张蓝色可合成一张蓝+
同种族的三张蓝+可以合成一张紫(可以选择合成其中蓝+的某个紫色)
紫+同英雄的两张紫色可以合成一张紫+
同种族的三张紫+可以合成一张黄(可选择合成其中紫+的某个角色)
黄+黄与同角色的紫+可以合成黄+
同种族的两张黄+可以合成一张红(可以选择合成其中黄+的某个角色)
红+同种族的红与黄+结合可以合成红+
红+与两张同英雄的紫+可以合成一张白
一张白与同英雄的紫+可以升星, 共5星

简单来说:

……

阅读全文

抽卡逻辑自述

抽卡逻辑自述

参考《剑与远征》

卡牌颜色以及合成

颜色合成
绿色可分解,得到粉尘和银瓜子, 最基础的卡
蓝+同样的三张蓝色可合成一张蓝+
同种族的三张蓝+可以合成一张紫(可以选择合成其中蓝+的某个紫色)
紫+同英雄的两张紫色可以合成一张紫+
同种族的三张紫+可以合成一张黄(可选择合成其中紫+的某个角色)
黄+黄与同角色的紫+可以合成黄+
同种族的两张黄+可以合成一张红(可以选择合成其中黄+的某个角色)
红+同种族的红与黄+结合可以合成红+
红+与两张同英雄的紫+可以合成一张白
一张白与同英雄的紫+可以升星, 共5星

简单来说:

……

阅读全文

层级时间轮 | skynet 定时器

skynet定时器

要解析一个程序代码,先了解数据结构,这是基础,再看函数。 拿skynet定时器举例子。

数据结构

//定时器事件 用于抛出定时器事件到消息队列里。理解这个数据结构需要先了解skynet的框架原理,
//不理解这个数据结构也不影响下面的论述
struct timer_event {
	int32_t handle;
	int session;
};

//定时器节点
struct timer_node {
	struct timer_node *next;
	uint32_t expire;
};

//定时器链表
struct link_list {
	struct timer_node head;
	struct timer_node *tail;
};

//层级时间轮
//存放所有定时器的地方
struct timer {
	struct link_list near[TIME_NEAR]; //最近的定时器
	struct link_list t[4][TIME_LEVEL];//更久远的定时器
	struct spinlock lock;           //全局锁
	uint32_t time;                  //当前滴答数
	uint32_t starttime;             //程序开始时间 绝对时间 时间戳,单位 s 秒
	uint64_t current;               //当前时间 相对时间 1cs 厘秒 =  10ms 毫秒
	uint64_t current_point;         //系统(pc)运行时间 相对时间 单位: cs 厘秒
};

上面定时器的基本数据结构了解了,再来看下面的函数, 基本思路是一样的。

……

阅读全文

层级时间轮 | skynet 定时器

skynet定时器

要解析一个程序代码,先了解数据结构,这是基础,再看函数。 拿skynet定时器举例子。

数据结构

//定时器事件 用于抛出定时器事件到消息队列里。理解这个数据结构需要先了解skynet的框架原理,
//不理解这个数据结构也不影响下面的论述
struct timer_event {
	int32_t handle;
	int session;
};

//定时器节点
struct timer_node {
	struct timer_node *next;
	uint32_t expire;
};

//定时器链表
struct link_list {
	struct timer_node head;
	struct timer_node *tail;
};

//层级时间轮
//存放所有定时器的地方
struct timer {
	struct link_list near[TIME_NEAR]; //最近的定时器
	struct link_list t[4][TIME_LEVEL];//更久远的定时器
	struct spinlock lock;           //全局锁
	uint32_t time;                  //当前滴答数
	uint32_t starttime;             //程序开始时间 绝对时间 时间戳,单位 s 秒
	uint64_t current;               //当前时间 相对时间 1cs 厘秒 =  10ms 毫秒
	uint64_t current_point;         //系统(pc)运行时间 相对时间 单位: cs 厘秒
};

上面定时器的基本数据结构了解了,再来看下面的函数, 基本思路是一样的。

……

阅读全文

A*算法 | Lua版本

A*算法思路

寻路步骤

  1. 从起点A开始, 把它作为待处理的方格存入一个"开启列表", 开启列表就是一个等待检查方格的列表.
  2. 寻找起点A周围可以到达的方格, 将它们放入"开启列表", 并设置它们的"父方格"为A.
  3. 从"开启列表"中删除起点 A, 并将起点 A 加入"关闭列表", “关闭列表"中存放的都是不需要再次检查的方格
  4. 从 “开启列表” 中选择 F 值最低的方格 C (绿色起始方块 A 右边的方块), 把它从 “开启列表” 中删除, 并放到 “关闭列表” 中.
  5. 检查它所有相邻并且可以到达 (障碍物和 “关闭列表” 的方格都不考虑) 的方格. 如果这些方格还不在 “开启列表” 里的话, 将它们加入 “开启列表”, 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 “父方格” 为 C.
  6. 如果某个相邻方格 D 已经在 “开启列表” 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 “父方格” 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.
  7. 当我们发现 “开始列表” 里出现了目标终点方块的时候, 说明路径已经被找到.

对每个格子怎么走,往哪个方向走有一个很重要的函数-估价函数, 公式如下:

……

阅读全文

Skynet源码赏析三 | 服务启动

skynet启动一个C服务

查看怎么启动C服务的最好的办法是打断点,看源码。

启动logger日志服务

我拿日志服务来举例子,怎么启动日志服务的呢?

  1. 传参数name = "logger", param = nullstruct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);;
  2. 从modules服务模块中取出名字为 logger的服务, struct skynet_module * result = _query(name);;
  3. 如果找不到则从cpath目录中找对应的.so文件,打开服务,并且存储到modules模块中。使用的函数是static void * _try_open(struct modules *m, const char * name);
  4. 创建服务实例m->create(),放到上下文skynet_contextinst中;
  5. skynet_context放到skynet_context list中, skynet_handle_register(ctx);;
  6. 创建logger服务对应的次级消息队列struct message_queue *queue,并且把消息队列push到全局消息队列中skynet_mq_create(ctx->handle);skynet_globalmq_push(queue);;
  7. 服务实例初始化m->init(),int r = skynet_module_instance_init(mod, inst, ctx, param);

总结下来,启动一个服务的过程就是,1.获取服务;2.创建服务实例;3.创建服务对应的上下文,并且把上下文放到skynet_context list管理模块中;4.绑定次级消息队列与对应的服务;5.服务初始化; 这五步过程。

……

阅读全文

Skynet源码赏析二 | 基础数据结构

module管理模块

我们所写的C服务编译成.so文件后放在cpath变量路径下,程序会加载路径cpath = root.."cservice/?.so"下的.so文件,通过static void * _try_open(struct modules *m, const char * name)函数第一次打开,然后被加载到全局变量static struct modules * M中。

……

阅读全文

Skynet源码赏析一 | 启动初始化的过程

入口文件

skynet_main.c

加载配置

struct skynet_config config;
config.thread =  optint("thread",8);
config.module_path = optstring("cpath","./cservice/?.so");
config.harbor = optint("harbor", 1);
config.bootstrap = optstring("bootstrap","snlua bootstrap");
config.daemon = optstring("daemon", NULL);
config.logger = optstring("logger", NULL);
config.logservice = optstring("logservice", "logger");
config.profile = optboolean("profile", 1);

从上述代码可以看到,配置都已经加载到变量struct skynet_config config;中了。

初始化

  1. 初始化skynet_context管理模块, skynet_handle_init(config->harbor);;
  2. 初始化static struct global_queue *Q消息队列, skynet_mq_init();;
  3. 初始化static struct modules * M管理模块, skynet_module_init(config->module_path);;
  4. 初始化定时器, skynet_timer_init();;

启动C服务

  1. 启动logger日志服务, struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);;
  2. 启动 snlua服务, ``;
  3. 通过snlua服务启动boostrap服务, bootstrap(ctx, config->bootstrap);;
  4. 启动线程:
  • 监听线程,检测线程是否阻塞。 create_thread(&pid[0], thread_monitor, m);;
  • 启动定时器线程。create_thread(&pid[1], thread_timer, m);;
  • 启动socket线程。create_thread(&pid[2], thread_socket, m);
  • 启动工作线程,用于lua服务消息的调度。create_thread(&pid[i+3], thread_worker, &wp[i]);;

启动lua服务

默认启动的lua服务,在boostrap.lua文件中可以看到都是通过snlua服务启动的。

……

阅读全文

FPS游戏 | 实现移动功能

三种实现FPS Controller的区别

  1. Transform Translate 允许移动物理,但无物理碰撞
  2. Rigid body + Capsule Collider 符合物理学 不会鬼穿墙 无法滞空运动 可与Physics Object 交互
  3. Charactor Controller 不会鬼穿墙 提供的API相对多 无法与Physic objects互动 可以滞空运动(太空人) 提供多种运动效果例如 slopes steps 等

Rigid body + Capsule Collider

控制摄像头旋转 FPMouseLook.cs 跟main camera 绑定

……

阅读全文

《仙剑奇侠传》编译运行

概述

今天在github上找到了《仙剑奇侠传》的rpg单机游戏sdlpalsdlpal 是一个开源项目,基于sdl把仙剑奇侠传重写了一遍。 下面是我的编译过程总结。

环境

  • win10- sdl1.2- sdlpal- vs2017- DirectX SDK10 下载sdlpal
$ git clone https://github.com.cnpmjs.org/sdlpal/sdlpal.git

下载sdl1.2

……

阅读全文