精华内容
下载资源
问答
  • 成就系统

    2018-10-21 22:39:03
    成就系统——观察者模式 百无聊赖,周末来写一个博客吧。苦于前几次去面试都被问到同一个问题,今天来好好想一下,该怎么做,怎么做才会更好一些。 游戏中的成就系统,是一个根据一些特定事件去完成一些目标,然后...

    成就系统——观察者模式

    百无聊赖,周末来写一个博客吧。苦于前几次去面试都被问到同一个问题,今天来好好想一下,该怎么做,怎么做才会更好一些。

    游戏中的成就系统,是一个根据一些特定事件去完成一些目标,然后获得奖励的玩法。游戏中的各种事件遍布游戏各处,成就类型又是多样化的。

    先说一下我自己的做法,在游戏中成就是和玩家相关的,所以直接在玩家的系统组件中添加了一个成就系统。我们要做的就是在成就系统中有一个事件触发的接口,所有的事件触发都会调用到这个地方,然后通过许许多多的事件定义来区分各个成就的处理(当然这里的前提是玩家只有在线的时候才能获得成就)。

    void onNotify(Event event,int param, int num);
    

    所以我理解的成就系统就是对一些成就类型的特定参数的计数(比如对进入场景类型,特定参数是A场景)。

    struct Achievement
    {
    	int id_;
    	int param_;
    	int num_;
    	Event event_;
    };
    

    但是,游戏里还包含这样的需求,比如说这个目标是进入A场景,那个目标是进入B场景。一开始的时候我们试图使用一个参数 param_来给他们找一个共同点。但是后来仍然失败了,然后就不得不扩展到param1_param2_

    感觉这是一种比较通常的做法了,但是有一天我看了项目的技能使用的做法,我就想改进我的成就系统了。技能释放是用的是一个消息队列实现的。具体做法是,释放技能者(游戏中的一些实体)将释放的技能添加到消息队列线程,消息队列线程管理消息列表,以确定消息什么时候以及,被哪个对象接收。消息队列的好处是解耦了消息释放者与接受者关系,而且还能保证消息的有序性。

    学习设计模式就是让你和其他同类从业者交流的时候能有一个统一的名字来指明同一种系统结构的实现方式,这个消息队列模式在《游戏编程模式》里也有提到,里面讲的是事件队列。但我现在还没搞清楚事件队列和消息队列有什么不一样,看上去很像同一个东西。

    就这样在面对面试官的一个致命问题时,我展现出了灵活的一面——你觉得这个系统还有什么地方做的不够好吗?如果有,有想过怎么改进吗?这个问题我本来是想用消息队列或者事件队列来改进优化的,但现在看来这完全是一种错误的想法。当然我也有理由:同步调用会阻塞当前程序执行,要等到所有调用结束后才会继续执行,而且当前成就也许会触发其他的游戏事件,这样就加大了调用的深度;所以我的理由就是降低调用深度。(但事实上调用深度很大的的只是少数情况)

    比较一下事件队列观察者最大的不同在于事件队列是使用异步处理的方式解耦了事件触发者和事件关注者,而观察者通常是同步调用的。我们想一下这个情况,在游戏角色获得一个物品时,要触发一个成就事件的改变,但是玩家在获得这个物品后马上又使用了它;这就导致,系统在接收到物品增加的消息时再去查看这个物品,发现它已经不存在了。

    以上我成就系统是基于玩家组件方式实现,当然还可以基于系统组件来实现,这时候我想*onNotify()*就要换一种写法,把Actor指针也传进去。

    void onNotify(const Actor actor, Event event,int param, int num);
    

    the end…

    												2018年10月21日
    
    展开全文
  • 先简单介绍下要讲的任务系统,因为游戏内容不多,所以目前的任务都比较简单,与其说是任务系统,不如叫成就系统.但是就算复杂的MMORPG游戏的任务系统,也可以按照此思路来做. 这次我只实现了两大类任务(成就任务、每日...

    结合目前的塔防项目,介绍一下游戏开发中的 任务系统

    先简单介绍下要讲的任务系统,因为游戏内容不多,所以目前的任务都比较简单,与其说是任务系统,不如叫成就系统.但是就算复杂的MMORPG游戏的任务系统,也可以按照此思路来做.

    这次我只实现了两大类任务(成就任务、每日任务)

    先看下任务系统的配置表

    任务ID 描述 父类型 子类型 目标值 前置任务 奖励ID 奖励数量
    ID Txt P_Type S_Type Value Pre_ID 1 1001
    1 今日获得金币50000 P2 S6 50000 0 1 1002
    2 今日通关5关关卡 P2 S4 5 0 1 1003
    3 今日杀怪达到100 P2 S5 100 0 1 1004
    101 累积金币达到10000 P1 S1 10000 0 1 1005
    102 累积金币达到50000 P1 S1 50000 101 1 1006
    103 累积金币达到100000 P1 S1 100000 102 1 1007
    201 通关关卡10 P1 S2 10 0 1 1008
    202 通关关卡20 P1 S2 20 201 1 1009
    203 通关关卡40 P1 S2 40 202 1 1010
    301 累积杀怪达到100 P1 S3 100 0 1 1011
    302 累积杀怪达到200 P1 S3 200 301 1 1012
    303 累积杀怪达到500 P1 S3 500 302 1 1013

    任务ID:唯一id

    文本:显示的任务描述

    父类型:1.数值 2.每日任务

    子类型:(1.累积金币 2.关卡 3.累积杀怪 4.今日通关 5.今日杀怪 6.今日获得金币)

    目标值:任务需达到的数量

    前置任务:前置任务(根据前置任务是否完成 决定是否显示任务)

    奖励ID:奖励道具的id (目前我只有一种游戏货币)

    奖励数量:奖励道具的数量

    上代码→

    --定义枚举类型:
    --子类型 (1.累积金币 2.关卡 3.累积杀怪 4.今日通关 5.今日杀怪 6.今日获得金币)
    TaskSType = { 
    	S1 = "Total_Money",
    	S2 = "Level",
    	S3 = "Total_Monster",
    	S4 = "Daily_Level",
    	S5 = "Daily_Monster",
    	S6 = "Daily_Money",
    }
    --任务管理类
    local TaskModel = class("TaskModel", XXModel)
    
    function TaskModel:init()
    	self.doneTaskList = {} --已经完成并领取的任务列表
    	self.taskList = {} --任务列表
    
    	self.totalMoney = 0 --累计获得金币
    	self.totalMonster = 0 --累计杀怪
    
    	self.dailyLevel = 0; --今日通过关卡
    	self.dailymonster= 0; --今日杀怪
    	self.dailymoney = 0; --今日获得金币
    
    	--初始化任务数值 key:子类型
    	self.taskCountList = {}
    	for _,v in pairs(TaskSType) do
    		self.taskCountList[v] = 0
    	end
    	self:resetTaskList()
    end
    --初始化显示任务列表
    function TaskModel:resetTaskList()
    	self.taskList = {}
    	for k,v in pairs(XXIO.allData["task"]) do
    		if self.doneTaskList[v.ID] == nil then  --未领取
    			if v.Pre_ID == "0" then --初始任务
    				self.taskList[v.ID] = v.ID
    			else
    				if self.doneTaskList[v.Pre_ID] ~= nil then --前置任务已完成
    					self.taskList[v.ID] = v.ID
    				end
    			end
    		end
    	end
    end
    --任务完成
    function TaskModel:doneTask(tid)
    	self.doneTaskList[tid] = tid
    	self:resetTaskList()
    	self:update()
    end
    --所需要维护的数据结构,对应到表中每个子类型
    function TaskModel:resetTaskValue()
    	local userModel = XXMVCManager:getModel("User")
    	self.taskCountList[TaskSType.S1] = self.totalMoney
    	self.taskCountList[TaskSType.S2] = userModel.level
    	self.taskCountList[TaskSType.S3] = self.totalMonster
    	self.taskCountList[TaskSType.S4] = self.dailyLevel
    	self.taskCountList[TaskSType.S5] = self.dailymonster
    	self.taskCountList[TaskSType.S6] = self.dailymoney
    	self:update()
    end
    --判断列表中是否有已经完成的任务
    function TaskModel:hasDoneTask()
    	for _,v in pairs(self.taskList) do
    		local info = XXIO.allData["task"][v] --配置表
    		local curValue = self.taskCountList[TaskSType[info.S_Type]]
    		if tonumber(curValue) >= tonumber(info.Value) then
    			return true
    		end
    	end
    	return true
    end
    
    function TaskModel:appendLevel() --数值改变
    	self.dailyLevel = self.dailyLevel + 1
    	self:resetTaskValue()
    end
    
    function TaskModel:appendMonster(money) --数值改变
    	self.totalMoney = self.totalMoney + money
    	self.totalMonster = self.totalMonster + 1
    	self.dailymoney = self.dailymoney + money
    	self.dailymonster = self.dailymonster + 1
    	self:resetTaskValue()
    end
    
    return TaskModel

    1.枚举值我使用的字符串,这点的好处就是可以一份代码终身试用.拿来当key,不要太香哦

    2. resetTaskList()

    根据完成列表doneTaskList的内容,序列化出来需要显示的任务

    3. doneTask()

    完成任务调用,完成的任务添加到完成列表doneTaskList,然后调用 resetTaskList

    4. resetTaskValue()   ※※※※※※※

    这个方法里面主要做的是整个任务系统在外部唯一要维护的列表,就是你各类任务所对应的数值

    我这里有6种类型的数据,只要维护好这个数值,那其他的地方不管你任务多少,都不需要在进行修改了

    5. hasDoneTask()

    看是否有已经完成了的任务,用于主界面任务按钮上面的红点显示

    6.appendLevel  appendMonster

    这些方法都是为了维护那个数据列表的

     

    基本就是这些,这个思路可以用于绝大多数任务系统,无非就是做一些修改

    比如大型RPG中的:

    • 击杀某个怪物
    • 移动到某个点
    • 采摘某个道具
    • 跟某个npc对话
    • 进入某个副本
    • 等等等等...

    有其他问题可以留言交流!

     

     

     

     

    展开全文
  • 成就系统实现(三)-架构设计

    千次阅读 2018-01-16 19:12:02
    之前上网找过一些关于成就系统设计的文档,推荐的都是事件驱动模型,因此底层设计也基于这个模型来做,由业务方产生可能触发成就的事件,统一放到一个队列里面,由线程去取队列里面的事件,进行分发处理。...

    1.技术选型

    之前上网找过一些关于成就系统设计的文档,推荐的都是事件驱动模型,因此底层设计也基于这个模型来做,由业务方产生可能触发成就的事件,统一放到一个队列里面,由线程去取队列里面的事件,进行分发处理。

    技术点:

    1.1 事件驱动模型

    1.2 disruptor 号称百万流量的一个高并发处理框架

    2.数据库设计

    2.1 成就包配置表
    CREATE TABLE `achievement_package` (
      `achievement_package_id` varchar(32) NOT NULL COMMENT '主键id',
      `name` varchar(32) NOT NULL COMMENT '包名称',
      `start_time` bigint(20) NOT NULL COMMENT '开始时间',
      `end_time` bigint(20) NOT NULL COMMENT '结束时间',
      `create_time` bigint(20) NOT NULL COMMENT '创建时间',
      `op_time` bigint(20) NOT NULL COMMENT '更新时间',
      `last_ver` int(11) NOT NULL COMMENT '版本号',
      `is_valid` tinyint(4) NOT NULL COMMENT '是否有效 0-失效 1-有效 ',
      `name_code` varchar(32) NOT NULL DEFAULT '' COMMENT '成就包名国际化编码',
      PRIMARY KEY (`achievement_package_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成就包配置表'

    2.2 成就模板表
    CREATE TABLE `achievement_template` (
      `achievement_template_id` varchar(32) NOT NULL COMMENT '主键id',
      `name` varchar(32) NOT NULL COMMENT '成就名称',
      `name_code` varchar(32) NOT NULL COMMENT '成就名称国际化编码',
      `context` varchar(64) DEFAULT NULL COMMENT '描述',
      `context_code` varchar(64) DEFAULT NULL COMMENT '描述国际化编码',
      `reward` varchar(64) NOT NULL COMMENT '奖励 json',
      `pic_url` varchar(255) DEFAULT NULL COMMENT '成就图片 status-url',
      `conditions` text NOT NULL COMMENT '达成条件 json',
      `achievement_package_id` varchar(32) NOT NULL COMMENT '所属成就包',
      `start_time` bigint(20) NOT NULL COMMENT '成就开始时间',
      `end_time` bigint(20) NOT NULL COMMENT '成就结束时间',
      `create_time` bigint(20) NOT NULL COMMENT '创建时间',
      `op_time` bigint(20) NOT NULL COMMENT '更新时间',
      `last_ver` int(11) NOT NULL COMMENT '版本号',
      `is_valid` tinyint(4) NOT NULL COMMENT '是否有效 0-失效 1-有效 ',
      PRIMARY KEY (`achievement_template_id`),
      KEY `udx_ach_pack_id` (`achievement_package_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成就模板表' 

    2.3 成就记录表
    CREATE TABLE `achievement_record` (
      `achievement_record_id` varchar(32) NOT NULL COMMENT '主键id',
      `customer_register_id` varchar(32) DEFAULT NULL COMMENT '小二id',
      `achievement_template_id` varchar(32) NOT NULL COMMENT '成就id',
      `fire_seed` int(11) NOT NULL COMMENT '火种数',
      `finish_time` bigint(20) NOT NULL COMMENT '完成时间',
      `status` smallint(6) NOT NULL COMMENT '状态 0-未开始 1-进行中 2-已完成 3-进行中已过期 4-未完成已过期',
      `schedule` text COMMENT '进度',
      `create_time` bigint(20) NOT NULL COMMENT '创建时间',
      `op_time` bigint(20) NOT NULL COMMENT '更新时间',
      `last_ver` int(11) NOT NULL COMMENT '版本号',
      `is_valid` tinyint(4) NOT NULL COMMENT '是否有效 0-失效 1-有效 ',
      `achievement_package_id` varchar(32) NOT NULL COMMENT '成就包Id',
      PRIMARY KEY (`achievement_record_id`),
      UNIQUE KEY `udx_cus_ach_id` (`customer_register_id`,`achievement_template_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户成就记录表'

    3.主要类图



    主要类讲解:
    1.AchievementEvent (成就事件基类)
     /**
         * 店铺Id
         */
        private String entityId;
        /**
         * 用户Id集合 完成事件的人
         */
        private List<String> customerRegisterIds;
        /**
         * 事件来源 1-APP 2-H5 {@link AchievementConstant.EventSource}
         */
        private int source;
        /**
         * 事件类型 see{@link AchievementEnum}
         */
        private int type;
        /**
         * 成就Id (封装每个handler里面对应的成就Id)
         */
        private String achievementId;
        /**
         * 队列名
         */
        private String disruptorName;
    由基类派生出多个成就事件子类

    2.EventClient (事件分发器)
    public class EventClient implements IEventClient, ApplicationContextAware {
    
        private ApplicationContext applicationContext;
    
        /**
         * 获得上下文
         *
         * @param applicationContext
         * @throws BeansException
         */
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    
        /**
         * 发布事件
         *
         * @param event
         * @return
         */
        public Boolean publish(AchievementEvent event) {
            String eventName = StringUtils.uncapitalize(event.getClass().getSimpleName());
            IEventPublisher publisher = (IEventPublisher) applicationContext.getBean(eventName + "Publisher");
            if (publisher != null) {
                return publisher.publish(event);
            }
            return false;
        }
    }
    这个类的作用将对应的事件分发给对应的事件发布器处理

    3.IEventPublisher(事件发布)

    public interface IEventPublisher<T extends AchievementEvent> {
        /**
         * 发布事件
         * @param event
         * @return
         */
        boolean publish(T event);
    }
    每种类型的事件都有各自的发布器,实现这个接口
    举例:OrderEventPublisher(订单事件发布器)
    public class OrderEventPublisher implements InitializingBean, IEventPublisher<OrderEvent> {
    
        private static final EventTranslatorOneArg<OrderEvent, OrderEvent> translator = (OrderEvent event, long sequence,
                                                                                         OrderEvent arg0) -> {
            event.setDisruptorName(arg0.getDisruptorName());
            event.setEntityId(arg0.getEntityId());
            event.setCustomerRegisterIds(arg0.getCustomerRegisterIds());
            event.setOrderId(arg0.getOrderId());
            event.setAction(arg0.getAction());
            event.setOrderFee(arg0.getOrderFee());
            event.setType(arg0.getType());
            event.setSource(arg0.getSource());
            event.setOrderType(arg0.getOrderType());
        };
        private static Logger log = LoggerFactory.getLogger(OrderEventPublisher.class);
        private Disruptor<OrderEvent> disruptor;
    
        @Resource
        private WelcomeHandler welcomeHandler;
        @Resource
        private LoveProverbsHandler loveProverbsHandler;
        @Resource
        private TechnologyHouseHandler technologyHouseHandler;
        @Resource
        private CookeryGodPassHandler cookeryGodPassHandler;
    
        private int bufferSize;
        private RingBuffer ringBuffer;
    
    
        @Override
        public void afterPropertiesSet() throws Exception {
            disruptor = new Disruptor<>(() -> new OrderEvent(),
                    4096, new EventHandlerThreadFactory("OrderEvent"), ProducerType.MULTI, new BlockingWaitStrategy());
            disruptor.handleEventsWith(welcomeHandler, loveProverbsHandler, technologyHouseHandler).then(cookeryGodPassHandler);
            disruptor.setDefaultExceptionHandler(new AchieveEventExceptionHandler("OrderEventDisruptor"));
            ringBuffer = disruptor.start();
            bufferSize = ringBuffer.getBufferSize();
        }
    
        @Override
        public boolean publish(OrderEvent event) {
            event.setDisruptorName("OrderEventDisruptor");
            if (ringBuffer.remainingCapacity() < bufferSize * 0.01) {
                log.warn("OrderEventDisruptor size = " + ringBuffer.remainingCapacity());
            }
            return ringBuffer.tryPublishEvent(translator, event);//发布事件;
        }
    }
    每个事件发布器管理自己的一个disruptor队列,存放对应的事件,在初始化的时候设置事件的监听器,也就是对应的成就处理类
    之后的publish就调用ringbuffer的方法,有兴趣的可以研究下disruptor源码

    4.AbstractAchieveHandler(成就处理基类)

    /**
         * 执行事件
         *
         * @param event
         * @param sequence
         * @param endOfBatch
         * @throws Exception
         */
        @Override
        public void onEvent(AchievementEvent event, long sequence, boolean endOfBatch) throws Exception {
            Logger.warn("{} {}  handle" +
                    " event event = {}, 位于队列序号={}", event.getDisruptorName(), this.getClass().getSimpleName(), event.toString(), sequence);
            if (event == null || CollectionUtils.isEmpty(event.getCustomerRegisterIds())) {
                LoggerUtil.error(FireMemberLoggerFactory.ACHIEVEMENT_LOGGER, LogMarker.ACHIEVEMENT_HANDLER,
                        "valid param fail event={}", event != null ? event.toString() : null);
                return;
            }
            if (!checkEvent(event)) {
                return;
            }
            for (String customerRegisterId : event.getCustomerRegisterIds()) {
                AchievementDTO achievementDTO = checkAchievement(customerRegisterId, event);
                if (achievementDTO.getStatus() == AchievementConstant.RecordStatus.STATUS_COMPLETED && reward(achievementDTO)) {
                    sendMessage(achievementDTO);
                }
            }
        }
    主要采用了模板设计模式,大部分功能在基类里面定义好了,各自具体的成就类只需要继承基类,实现checkEvent方法就行
    举例:WelComeHandler
    public class WelcomeHandler extends AbstractAchieveHandler {
    
        private static String achievementId = "c5a968a3372c474794ef670fe9ad7132";
    
        @Override
        protected boolean checkEvent(AchievementEvent event) {
            //1.事件类型检验
            if (event instanceof OrderEvent) {
                OrderEvent orderEvent = (OrderEvent) event;
                if (orderEvent.getAction() == AchievementEnum.OrderActionEnum.SUBMIT.getCode() &&
                        AchievementConstant.OrderType.ORDER_SHOP.equals(orderEvent.getOrderType())) {
                    event.setAchievementId(achievementId);
                    return true;
                }
            }
            return false;
        }
    }
    checkEvent方法主要是判断成就是否达成,因为每个成就都有各自的条件,因此可变的部分由各自的子类去实现。

    就此,大致的一个框架设计 就出来了。






    展开全文
  • 游戏成就系统的实现

    千次阅读 2013-08-01 21:52:00
    在游戏系统中,我们需要给玩家设计出一个成就系统,满足玩家的荣誉感, 同时给玩家设定目标,除了荣誉感,达成某种成就,往往也会给玩家带来某种好处,例如声望值的提高。 获得成就需要一定的条件,也就是,玩家...

    在游戏系统中,我们需要给玩家设计出一个成就系统,满足玩家的荣誉感, 同时给玩家设定目标,除了荣誉感,达成某种成就,往往也会给玩家带来某种好处,例如声望值的提高。

    获得成就需要一定的条件,也就是,玩家需要完成某种指定的动作后,才可以获得需要的成就。下面我具体举一个成就的例子,说明成就系统的实现方式。

    成就名: 勇者无畏

    达成条件:战斗胜利次数到达10次

    战斗奖励:增加玩家的声望值。


    实现以上需求,需要注意的问题:

    A 需要统计玩家战斗的次数

    B 当成就达到的时候,不能重复达到

    C 完成成就后,可以增加玩家的声望值

    D 进行某种动作的时候,可能同时开启两个成就


    成就的完成流程:



    数据库设计:

    需要统计完成战斗次数的表:user_state


    我的成就表: user_achievement


    成就的配置表:

    * related_step_field: 如果字段可以对应到user_stats 这样就可以不要记录成就的完成度 当获取进度的时候 $this->_user['stats][related_step_field]


    类设计


    我的统计模型类:

    职能:记录需要统计的字段,如果字段发生改变,调用成就类里面的方法。

    <?php
    
    /**
     * 我的统计 模型
     *
     * @author zhangkai
     * $Id: Stats.php 4374 2013-07-30 07:26:26Z sunli $
     */
    
    class Model_User_Stats extends Model_User_Trait
    {
        /**
         * 子类构函
         *
         * @return void
         */
        protected function _initTrait()
        {
            $this->_prop = $this->DaoDs('UserStats')->get($this->_uid);
        }
    
        /**
         * 指定字段自增
         *
         * @return bool
         */
        public function increment($field, $offset = 1)
        {
            // 注意这里 需要给这个属性加成,否则,就算数据库里面的字段已经更新,还是不能获取到最新信息
            $this->_prop[$field] += $offset;
    
            return $this->DaoDs('UserStats')
                        ->where(array('uid' => $this->_uid))
                        ->increment($field, $offset);
        }
    
        // 增加主动胜利场数
        public function addTotalWinTimes($num = 1)
        {
            $this->increment('total_win_times', $num);
            $this->_user->achievement->checkBattleWinAchieve();
        }
    
    }

    我的成就类:

    职能:

    检测成就是否达成

    改变我的成就的进度

    成就达成后,给予相应的奖励

    /**
     * 我的成就
     *
     */
    
    class Model_User_Achievement extends Model_User_Trait
    {
        // 内部方法,用于判断是否达成了某种类型的成就
        private function _checkAchieve($field, $attrs)
        {
            $times = $this->_user['stats'][$field];
    
            $result = array();
    
            foreach ($attrs as $data) {
                if ($times >= $data['line']) {
                    $result[$data['achieve_id']] = $this->achieve($data['achieve_id']);
                }
            }
    
            return $result;
        }
    
        // 检测是否达成了战斗胜利的相关成就
        public function checkBattleWinAchieve()
        {
            $data = array();
    
        // 同时开启两个成就 line:表示成就需要的总进度
            $data[] = array('line' => 100, 'achieve_id' => 721003);
            $data[] = array('line' => 10, 'achieve_id' => 721001);
    
            return $this->_checkAchieve('total_win_times', $data);
        }
    
    
       
        // 达成成就(不可重复达成)
        public function achieve($achieveId)
        {
            // 已达成过直接退出 如果是第一次开启成就,则插入此成就
            if ($this->DaoDs('UserAchievement')->isCreated($this->_uid, $achieveId)) {
                return false;
            }
    
            // 成就详细
            $achieveInfo = Dao('Static_Achievement')->get($achieveId);
    
            // 实时弹出框
            $this->_user->tips->setTips('showGainAchievementTip', $achieveInfo);
    
            // 达成成就后 奖励声望
            $this->_user->base->addReputation($achieveInfo['reputation_award']);
        // 设置成就已经完成
            return $this->DaoDs('UserAchievement')->createAchieve($this->_uid, $achieveId);
        }
    }
    
    
    
    
    
    
    
    
    
    
    
    展开全文
  • Steam的成就系统还是比较简单好做的,大部分API都已经弄好了。 这里就把大概流程总结一下,方便以后查阅。 1、SteamWork的设置 在Steamworks网页的成就面板中,可以添加成就,默认是英文的。 点击新成就可以...
  • 成就系统框架设计

    千次阅读 2013-01-21 09:44:43
    成就系统配置 成就参数操作类型:1位 8位 16位 32位 AchivementGroup:同一种成就组成一组,比如杀死3个敌人,杀死10个敌人 Param1CheckType: 0:忽略 1:= 2:> 3:= 5: ModifyType:0:忽略 1:8位加 2:16位加 3:...
  • 成就系统实现(二)-功能分析

    千次阅读 2018-01-07 15:55:11
    成就系统的业务流程大概分为这三步,每一步都可以细分里面的内部细节功能1.1 操作触发成就按照成就类型 抽象出具体哪些事件可能会触发1.2 用户达成成就触发的事件是否满足成就的条件,可能某一个事件会触发多个成就...
  • U3D连接Steamworks SDK制作成就系统

    千次阅读 2018-08-28 15:54:47
    最近要做一个成就系统出来,研究了一个礼拜总算成功了,在这里把流程写出来以免日后还需要用到。 首先是安装steamwork,steamwork.net有详细的说明,嫌麻烦的可以直接看我这里: 在Github下载最新的版本:...
  • 如何做好游戏中的成就系统

    千次阅读 2017-02-15 13:08:44
    成就系统忠实记录了用户在游戏中的成长和精彩瞬间,Doctor Who:Legacy正是靠这个功能大幅提高了收入。
  • IAP跟成就系统的思路

    千次阅读 2012-09-25 14:45:39
    简单的话了两张类图,发现其实也没有说特别的复杂只是单纯的用一个Manager类做统一管理,让客户端使用代码时不用面对底层的实际成就系统或IAP的API只要将成就条件的代码或是IAP参数设定好,并交由管理类处理似乎就...
  • 成就系统实践

    千次阅读 2013-03-27 14:45:33
    目的实践跨平台的成就发布系统目标平台iOS, AndroidUMLCBAchievementExhibitor这个类,继承自观察者模式中的CBObserver类。因为可能根据不同的需求,会实践不同的扩充,像是国外用户可能较少使用微博,国内用户因为...
  • 成就系统这个舶来品,成为了继副本系统之后又一个被策划要求的标配了,最近的讨论愈发觉得成就系统本身的吸引力绝大部分取决于玩家自身素质。 成就在游戏里的地位,可以说是似有似无,但有充斥于任何一个边边角角的...
  • 高效的成就系统实现简介

    千次阅读 2011-02-18 16:49:00
    什么是成就成就系统可以理解为对某个角色成长过程重大事件的记录;玩家通过查看成就跟踪角色涉及的重大事件。成就的策划模版数据ID, 这个成就的唯一标识;Name, 命名;EventCategroy, 完成的事件对应的类型,类型是...
  • 成就系统实现(一)-概述

    千次阅读 2017-12-30 15:36:10
    需求 在一个完整的业务流程链路中,用户的某种行为可能会触发成就的达成,如果成就达成了,推送一个弹窗让用户感知。第一版设计有11种可以达成的成就。每个成就都有对应的奖励。...1.系统日均UV百万级别,成就
  • 再议成就系统框架设计

    千次阅读 2013-01-21 13:18:49
    1.Group组的Buff是靠严格的配置的偏移量计算出来的,这个太不可控了。Buff可能出在无法检查的策划小错误上 2.如果已经有N种类型的成就,而有一个新的需求,其事件是已有的...产生一种新的成就组,使用新的成就Bu
  • 想当年WOW推出成就系统的时候,我就很不争气的沉迷了,收集了无数的宠物啊,下了无数的本啊,都是TMD痛苦回忆啊!后来WLK的时候AFK了,还挺怀念自己的5000+的成就点数的,没想到,现在微软效仿WOW,在自己的编程软件...
  • 成就系统实现(四)-测试和总结

    千次阅读 2018-02-27 20:09:19
    完成架构设计之后,就开始进入了开发阶段,后续经过联调测试的调整,大概花了两个礼拜的时间,完成了开发和自测,代码已在github开源:https://github.com/caisl/achievement-system.git单元测试:@Test ...
  • 成就系统工程师的职业生涯

    千次阅读 2012-06-28 18:39:59
    这位大哥的话,让我感动!...中国是发展中国家,没有职业市场,一个人能成就什么,靠的是运气、胆量而不是技能、创造力。是这样么?历史有惊人的相似性,一句最具代表性的名言是:这个世界最不可思议的事
  • todo:
  • todo:
  • todo:
  • todo:
  • 戴尔 Vostro 3470 成就商务台式机, 采用intel 8代处理器(i3-8100), 此款台式机,有线,无线网卡默认都有。 硬盘无固态,采用1TB 东芝机械硬盘。 戴尔官网链接 一小伙伴,在京东购买后,需要安装Win7系统,那么...
  • 现在大多数笔记本都是uefi启动...安装完成后重启系统会自动出现grub引导,可以选择原先的win10系统,也可以选择新安装的ubuntu系统,这时候默认grub引导系统是ubuntu,可以通过修改grub默认引导顺序改变,教程自行搜索.
  • ## 戴尔灵越/成就 5402/5502 最新十一代处理器如何重装系统,如何解决安装过程中无法识别硬盘??? 相信大家最近遇到过,十一代的笔记本重装系统是在选择硬盘是显示没有驱动器,如下图所示: 这是因为我们少了一...
  • 我看鸿蒙操作系统

    万次阅读 多人点赞 2019-06-18 19:58:00
    华为宣布推出鸿蒙操作系统。其实我觉得能理解,但也蛮无奈的,所谓不得已而为之,google不提供后续版本授权,不提供新的支持,怎么办,硬着头皮也要上。有些自媒体说什么安卓慌...
  • 自1983年开始对地理信息标准开始系统研究,次年发表《资源与环境信息系统国家规范和标准研究报告》(俗称蓝皮书),这是我国第一个有关地理信息标准化的论著,对后来地理信息系统及其标准化工作产生了重要影响。...
  • 我国网格计算的最高成就——高性能计算环境和清华ACI系统 Monday, June 30 2003 3:12 PM 目前,网格计算在我国尚处于研究阶段,中国工程院院士、中科院计算技术研究所所长李国杰在谈到网格计算时说,“我们必须从...
  • [img=https://img-bbs.csdn.net/upload/202011/11/1605100524_512570.png][/img][img=https://img-bbs.csdn.net/upload/202011/11/1605100530_942332.png][/img] [b][i][/i][/b][color=#FF0000]之前还连蓝牙键盘用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 116,298
精华内容 46,519
关键字:

成就系统