存档

2017年1月 的存档

3dMax 制作动画技巧总结

2017年1月11日 没有评论
3dMax 制作动画技巧总结



基本操作

  • 打开默认动画面板:自定义->显示UI->显示轨迹栏
  • 调整帧的密度:Ctrl + Alt + 鼠标右键
  • 切换线框和实体显示: F3 。增加线框 F4
  • 多选子物体:选中多个根节点后 ctrl+PangDown
  • 合并多个Bip动作:动作->Biped应用程序->混合器->右键轨道->添加源->从文件导入->选择对应的Bip->右键界面中的骨骼名称->计算合成->将合成复制到Biped

Biped操作

  • 创建:Inspector中创建->系统->Biped
  • 修改:选中骨骼->Inspector中运动->Biped体型模式->结构
  • 添加额外骨骼:选中骨骼->Inspector中运动->Biped体型模式->结构->XTra
  • 选择子骨骼:双击父骨骼
  • 多Biped的模型需要创建链接。要注意体型模式下链接功能是失效的,因此设置链接需要切换体型模式。

动作操作

  • 镜像复制:运动->复制/粘贴->复制收集->姿态->复制姿态->向对面镜像粘贴
  • 通过IK添加父层关系:点击添加滑动关键帧来激活IK->选择IK对象指定父物体。
  • 脱手:将属性中的位置空间和旋转空间改为世界
  • 整体修改:使用层功能,进行叠加。如果需要限定范围修改,则应借助捕捉功能,抓取下面层的信息进行锁帧。
  • 加位移与去位移:加位移使用层,去位移使用运动->Biped->模式与显示->原地模式
  • 动作混合:在混合器中加入动作轨->载入动画片段->设置过滤骨骼。
  • 模拟定帧:添加时间扭曲->在动画上创建分割线->拖动分割线上部分调整缩放。
  • 动作序列:Biped->运动流模式->运动流->显示图形->创建多个剪辑->定义脚本->按顺序点击
  • 多人运动:复制一堆对象->运动流模式->共享运动流->新建->添加Bip->在多个对象中播放运动流(绿箭头)->确定->显示图形->弹出界面中选择随机开始剪辑->右侧创建随机运动->去掉创建统一运动->创建共享该运动流的所有Biped的运动。
  • 对于四元数的自动平滑如果有接受不了的效果,可以使用四元数/Euler->Euler来切换动画类型。按住Shift可以改单方向的键

流程

模型自检

  1. 模型检查:归为零点
  2. 顶点缝合:选中模型->修改->顶点->焊接->设置参数为0.01。之前与之后的数字应该完全一样。
  3. 加入ResetTransform: 实用工具->重置变换。修改面板中应出现x变换,右键点击->塌陷。

绑骨

  • 隐藏部件: 修改->多边形->选择相关面片->隐藏选定对象
  • 打开体型模式,关闭自动关键帧。选中模型,右键冻结模型

加骨

修改->添加修改器(编辑多边形)->选择“元素”->附加->选择骨骼点

蒙皮

  • 选中对应的模型->编辑修改器中添加蒙皮->在骨骼中选择添加->添加对应骨骼->更改封套范围进行权重设置
  • 封套长期显示:封套属性->封套可见性
  • 局部复制:选择部分封套->封套属性->复制/粘贴
  • 细修:封套->参数->(选择)勾选顶点,可在下面手动修改权重,也可以打开权重工具界面详细设置

蒙皮后调缩放

  1. 先用Box测量身高算出比例。点击实例应用->配置按钮集->蒙皮实用程序拖动到右侧。
  2. 点击上方层级管理器,创建一个新的层。在新层中点击蒙皮实用工具中的将蒙皮数据提取到网格。
  3. 创建一个虚拟体,将要缩放的模型链接到虚拟体上,然后可以整体缩放住模型和配件。Biped模型可以单独调整高度缩放。
  4. 为缩放后的模型创建蒙皮,并重新绑骨。在场景中选中缩放过的模型和数据模型,点击从网格导入到蒙皮数据,点击按名称匹配,点确定。

如果你觉得这篇文章对你有帮助,可以顺手点个,不但不会喜当爹,还能让更多人能看到它… 3dMax 制作动画技巧总结

分类: 未分类 标签:

Scroll Segmented Control(Swift)

2017年1月10日 没有评论

今天用了一个github上一个比较好用的Segmented Control但是发现不是我要效果,我需要支持scrollView。当栏目数量超过一屏幕,需要能够滑动。

由于联系作者没有回复,我就自己在其基础上增加了下scrollView的支持。

Scroll Segmented Control(Swift)

代码比较简单,直接在UIControl下写的。

其中有一个比较有意思的地方,IndicatorView下面放了一个titleMaskView作为mask。用来遮罩选用的titles标签。已达到过渡效果。

源代码:

//
//  SwiftySegmentedControl.swift
//  SwiftySegmentedControl
//
//  Created by LiuYanghui on 2017/1/10.
//  Copyright © 2017年 Yanghui.Liu. All rights reserved.
//

import UIKit

// MARK: - SwiftySegmentedControl
@IBDesignable open class SwiftySegmentedControl: UIControl {
    // MARK: IndicatorView
    fileprivate class IndicatorView: UIView {
        // MARK: Properties
        fileprivate let titleMaskView = UIView()
        fileprivate let line = UIView()
        fileprivate let lineHeight: CGFloat = 2.0
        fileprivate var cornerRadius: CGFloat = 0 {
            didSet {
                layer.cornerRadius = cornerRadius
                titleMaskView.layer.cornerRadius = cornerRadius
            }
        }
        override open var frame: CGRect {
            didSet {
                titleMaskView.frame = frame
                let lineFrame = CGRect(x: 0, y: frame.size.height - lineHeight, width: frame.size.width, height: lineHeight)
                line.frame = lineFrame
            }
        }

        open var lineColor = UIColor.clear {
            didSet {
                line.backgroundColor = lineColor
            }
        }

        // MARK: Lifecycle
        init() {
            super.init(frame: CGRect.zero)
            finishInit()
        }
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            finishInit()
        }
        fileprivate func finishInit() {
            layer.masksToBounds = true
            titleMaskView.backgroundColor = UIColor.black
            addSubview(line)
        }

        override open func layoutSubviews() {
            super.layoutSubviews()

        }
    }

    // MARK: Constants
    fileprivate struct Animation {
        fileprivate static let withBounceDuration: TimeInterval = 0.3
        fileprivate static let springDamping: CGFloat = 0.75
        fileprivate static let withoutBounceDuration: TimeInterval = 0.2
    }
    fileprivate struct Color {
        fileprivate static let background: UIColor = UIColor.white
        fileprivate static let title: UIColor = UIColor.black
        fileprivate static let indicatorViewBackground: UIColor = UIColor.black
        fileprivate static let selectedTitle: UIColor = UIColor.white
    }

    // MARK: Error handling
    public enum IndexError: Error {
        case indexBeyondBounds(UInt)
    }

    // MARK: Properties
    /// The selected index
    public fileprivate(set) var index: UInt
    /// The titles / options available for selection
    public var titles: [String] {
        get {
            let titleLabels = titleLabelsView.subviews as! [UILabel]
            return titleLabels.map { $0.text! }
        }
        set {
            guard newValue.count > 1 else {
                return
            }
            let labels: [(UILabel, UILabel)] = newValue.map {
                (string) -> (UILabel, UILabel) in

                let titleLabel = UILabel()
                titleLabel.textColor = titleColor
                titleLabel.text = string
                titleLabel.lineBreakMode = .byTruncatingTail
                titleLabel.textAlignment = .center
                titleLabel.font = titleFont
                titleLabel.layer.borderWidth = titleBorderWidth
                titleLabel.layer.borderColor = titleBorderColor
                titleLabel.layer.cornerRadius = indicatorView.cornerRadius

                let selectedTitleLabel = UILabel()
                selectedTitleLabel.textColor = selectedTitleColor
                selectedTitleLabel.text = string
                selectedTitleLabel.lineBreakMode = .byTruncatingTail
                selectedTitleLabel.textAlignment = .center
                selectedTitleLabel.font = selectedTitleFont

                return (titleLabel, selectedTitleLabel)
            }

            titleLabelsView.subviews.forEach({ $0.removeFromSuperview() })
            selectedTitleLabelsView.subviews.forEach({ $0.removeFromSuperview() })

            for (inactiveLabel, activeLabel) in labels {
                titleLabelsView.addSubview(inactiveLabel)
                selectedTitleLabelsView.addSubview(activeLabel)
            }

            setNeedsLayout()
        }
    }
    /// Whether the indicator should bounce when selecting a new index. Defaults to true
    public var bouncesOnChange = true
    /// Whether the the control should always send the .ValueChanged event, regardless of the index remaining unchanged after interaction. Defaults to false
    public var alwaysAnnouncesValue = false
    /// Whether to send the .ValueChanged event immediately or wait for animations to complete. Defaults to true
    public var announcesValueImmediately = true
    /// Whether the the control should ignore pan gestures. Defaults to false
    public var panningDisabled = false
    /// The control's and indicator's corner radii
    @IBInspectable public var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.cornerRadius = newValue
            indicatorView.cornerRadius = newValue - indicatorViewInset
            titleLabels.forEach { $0.layer.cornerRadius = indicatorView.cornerRadius }
        }
    }
    /// The indicator view's background color
    @IBInspectable public var indicatorViewBackgroundColor: UIColor? {
        get {
            return indicatorView.backgroundColor
        }
        set {
            indicatorView.backgroundColor = newValue
        }
    }
    /// Margin spacing between titles. Default to 33.
    @IBInspectable public var marginSpace: CGFloat = 33 {
        didSet { setNeedsLayout() }
    }
    /// The indicator view's inset. Defaults to 2.0
    @IBInspectable public var indicatorViewInset: CGFloat = 2.0 {
        didSet { setNeedsLayout() }
    }
    /// The indicator view's border width
    public var indicatorViewBorderWidth: CGFloat {
        get {
            return indicatorView.layer.borderWidth
        }
        set {
            indicatorView.layer.borderWidth = newValue
        }
    }
    /// The indicator view's border width
    public var indicatorViewBorderColor: CGColor? {
        get {
            return indicatorView.layer.borderColor
        }
        set {
            indicatorView.layer.borderColor = newValue
        }
    }
    /// The indicator view's line color
    public var indicatorViewLineColor: UIColor {
        get {
            return indicatorView.lineColor
        }
        set {
            indicatorView.lineColor = newValue
        }
    }
    /// The text color of the non-selected titles / options
    @IBInspectable public var titleColor: UIColor  {
        didSet {
            titleLabels.forEach { $0.textColor = titleColor }
        }
    }
    /// The text color of the selected title / option
    @IBInspectable public var selectedTitleColor: UIColor {
        didSet {
            selectedTitleLabels.forEach { $0.textColor = selectedTitleColor }
        }
    }
    /// The titles' font
    public var titleFont: UIFont = UILabel().font {
        didSet {
            titleLabels.forEach { $0.font = titleFont }
        }
    }
    /// The selected title's font
    public var selectedTitleFont: UIFont = UILabel().font {
        didSet {
            selectedTitleLabels.forEach { $0.font = selectedTitleFont }
        }
    }
    /// The titles' border width
    public var titleBorderWidth: CGFloat = 0.0 {
        didSet {
            titleLabels.forEach { $0.layer.borderWidth = titleBorderWidth }
        }
    }
    /// The titles' border color
    public var titleBorderColor: CGColor = UIColor.clear.cgColor {
        didSet {
            titleLabels.forEach { $0.layer.borderColor = titleBorderColor }
        }
    }

    // MARK: - Private properties
    fileprivate let contentScrollView: UIScrollView = {
        let scrollView = UIScrollView()
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        return scrollView
    }()
    fileprivate let titleLabelsView = UIView()
    fileprivate let selectedTitleLabelsView = UIView()
    fileprivate let indicatorView = IndicatorView()
    fileprivate var initialIndicatorViewFrame: CGRect?

    fileprivate var tapGestureRecognizer: UITapGestureRecognizer!
    fileprivate var panGestureRecognizer: UIPanGestureRecognizer!

    fileprivate var width: CGFloat { return bounds.width }
    fileprivate var height: CGFloat { return bounds.height }
    fileprivate var titleLabelsCount: Int { return titleLabelsView.subviews.count }
    fileprivate var titleLabels: [UILabel] { return titleLabelsView.subviews as! [UILabel] }
    fileprivate var selectedTitleLabels: [UILabel] { return selectedTitleLabelsView.subviews as! [UILabel] }
    fileprivate var totalInsetSize: CGFloat { return indicatorViewInset * 2.0 }
    fileprivate lazy var defaultTitles: [String] = { return ["First", "Second"] }()
    fileprivate var titlesWidth: [CGFloat] {
        return titles.map {
            let statusLabelText: NSString = $0 as NSString
            let size = CGSize(width: width, height: height - totalInsetSize)
            let dic = NSDictionary(object: titleFont,
                                   forKey: NSFontAttributeName as NSCopying)
            let strSize = statusLabelText.boundingRect(with: size,
                                                       options: .usesLineFragmentOrigin,
                                                       attributes: dic as? [String : AnyObject],
                                                       context: nil).size
            return strSize.width
        }
    }

    // MARK: Lifecycle
    required public init?(coder aDecoder: NSCoder) {
        index = 0
        titleColor = Color.title
        selectedTitleColor = Color.selectedTitle
        super.init(coder: aDecoder)
        titles = defaultTitles
        finishInit()
    }
    public init(frame: CGRect,
                titles: [String],
                index: UInt,
                backgroundColor: UIColor,
                titleColor: UIColor,
                indicatorViewBackgroundColor: UIColor,
                selectedTitleColor: UIColor) {
        self.index = index
        self.titleColor = titleColor
        self.selectedTitleColor = selectedTitleColor
        super.init(frame: frame)
        self.titles = titles
        self.backgroundColor = backgroundColor
        self.indicatorViewBackgroundColor = indicatorViewBackgroundColor
        finishInit()
    }

    @available(*, deprecated, message: "Use init(frame:titles:index:backgroundColor:titleColor:indicatorViewBackgroundColor:selectedTitleColor:) instead.")
    convenience override public init(frame: CGRect) {
        self.init(frame: frame,
                  titles: ["First", "Second"],
                  index: 0,
                  backgroundColor: Color.background,
                  titleColor: Color.title,
                  indicatorViewBackgroundColor: Color.indicatorViewBackground,
                  selectedTitleColor: Color.selectedTitle)
    }

    @available(*, unavailable, message: "Use init(frame:titles:index:backgroundColor:titleColor:indicatorViewBackgroundColor:selectedTitleColor:) instead.")
    convenience init() {
        self.init(frame: CGRect.zero,
                  titles: ["First", "Second"],
                  index: 0,
                  backgroundColor: Color.background,
                  titleColor: Color.title,
                  indicatorViewBackgroundColor: Color.indicatorViewBackground,
                  selectedTitleColor: Color.selectedTitle)
    }


    fileprivate func finishInit() {
        layer.masksToBounds = true

        addSubview(contentScrollView)
        contentScrollView.addSubview(titleLabelsView)
        contentScrollView.addSubview(indicatorView)
        contentScrollView.addSubview(selectedTitleLabelsView)
        selectedTitleLabelsView.layer.mask = indicatorView.titleMaskView.layer

        tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(SwiftySegmentedControl.tapped(_:)))
        addGestureRecognizer(tapGestureRecognizer)

        panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(SwiftySegmentedControl.panned(_:)))
        panGestureRecognizer.delegate = self
        addGestureRecognizer(panGestureRecognizer)
    }
    override open func layoutSubviews() {
        super.layoutSubviews()
        guard titleLabelsCount > 1 else {
            return
        }

        contentScrollView.frame = bounds
        let allElementsWidth = titlesWidth.reduce(0, {$0 + $1}) + CGFloat(titleLabelsCount) * marginSpace
        contentScrollView.contentSize = CGSize(width: max(allElementsWidth, width), height: 0)

        titleLabelsView.frame = bounds
        selectedTitleLabelsView.frame = bounds

        indicatorView.frame = elementFrame(forIndex: index)

        for index in 0...titleLabelsCount-1 {
            let frame = elementFrame(forIndex: UInt(index))
            titleLabelsView.subviews[index].frame = frame
            selectedTitleLabelsView.subviews[index].frame = frame
        }
    }

    // MARK: Index Setting
    /*!
     Sets the control's index.

     - parameter index:    The new index
     - parameter animated: (Optional) Whether the change should be animated or not. Defaults to true.

     - throws: An error of type IndexBeyondBounds(UInt) is thrown if an index beyond the available indices is passed.
     */
    public func setIndex(_ index: UInt, animated: Bool = true) throws {
        guard titleLabels.indices.contains(Int(index)) else {
            throw IndexError.indexBeyondBounds(index)
        }
        let oldIndex = self.index
        self.index = index
        moveIndicatorViewToIndex(animated, shouldSendEvent: (self.index != oldIndex || alwaysAnnouncesValue))
        fixedScrollViewOffset(Int(self.index))
    }

    // MARK: Fixed ScrollView offset
    fileprivate func fixedScrollViewOffset(_ focusIndex: Int) {
        guard contentScrollView.contentSize.width > width else {
            return
        }

        let targetMidX = self.titleLabels[Int(self.index)].frame.midX
        let offsetX = contentScrollView.contentOffset.x
        let addOffsetX = targetMidX - offsetX - width / 2
        let newOffSetX = min(max(0, offsetX + addOffsetX), contentScrollView.contentSize.width - width)
        let point = CGPoint(x: newOffSetX, y: contentScrollView.contentOffset.y)
        contentScrollView.setContentOffset(point, animated: true)
    }

    // MARK: Animations
    fileprivate func moveIndicatorViewToIndex(_ animated: Bool, shouldSendEvent: Bool) {
        if animated {
            if shouldSendEvent && announcesValueImmediately {
                sendActions(for: .valueChanged)
            }
            UIView.animate(withDuration: bouncesOnChange ? Animation.withBounceDuration : Animation.withoutBounceDuration,
                           delay: 0.0,
                           usingSpringWithDamping: bouncesOnChange ? Animation.springDamping : 1.0,
                           initialSpringVelocity: 0.0,
                           options: [UIViewAnimationOptions.beginFromCurrentState, UIViewAnimationOptions.curveEaseOut],
                           animations: {
                            () -> Void in
                            self.moveIndicatorView()
            }, completion: { (finished) -> Void in
                if finished && shouldSendEvent && !self.announcesValueImmediately {
                    self.sendActions(for: .valueChanged)
                }
            })
        } else {
            moveIndicatorView()
            sendActions(for: .valueChanged)
        }
    }

    // MARK: Helpers
    fileprivate func elementFrame(forIndex index: UInt) -> CGRect {
        // 计算出label的宽度,label宽度 = (text宽度) + marginSpace
        // | <= 0.5 * marginSpace => text1 <= 0.5 * marginSpace => | <= 0.5 * marginSpace => text2 <= 0.5 * marginSpace => |
        // 如果总宽度小于bunds.width,则均分宽度 label宽度 = bunds.width / count
        let allElementsWidth = titlesWidth.reduce(0, {$0 + $1}) + CGFloat(titleLabelsCount) * marginSpace
        if allElementsWidth < width {
            let elementWidth = (width - totalInsetSize) / CGFloat(titleLabelsCount)
            return CGRect(x: CGFloat(index) * elementWidth + indicatorViewInset,
                          y: indicatorViewInset,
                          width: elementWidth,
                          height: height - totalInsetSize)
        } else {
            let titlesWidth = self.titlesWidth
            let frontTitlesWidth = titlesWidth.enumerated().reduce(CGFloat(0)) { (total, current) in
                return current.0 < Int(index) ? total + current.1 : total
            }
            let x = frontTitlesWidth + CGFloat(index) * marginSpace
            return CGRect(x: x,
                          y: indicatorViewInset,
                          width: titlesWidth[Int(index)] + marginSpace,
                          height: height - totalInsetSize)
        }
    }
    fileprivate func nearestIndex(toPoint point: CGPoint) -> UInt {
        let distances = titleLabels.map { abs(point.x - $0.center.x) }
        return UInt(distances.index(of: distances.min()!)!)
    }
    fileprivate func moveIndicatorView() {
        indicatorView.frame = titleLabels[Int(self.index)].frame
        layoutIfNeeded()
    }

    // MARK: Action handlers
    @objc fileprivate func tapped(_ gestureRecognizer: UITapGestureRecognizer!) {
        let location = gestureRecognizer.location(in: contentScrollView)
        try! setIndex(nearestIndex(toPoint: location))
    }
    @objc fileprivate func panned(_ gestureRecognizer: UIPanGestureRecognizer!) {
        guard !panningDisabled else {
            return
        }

        switch gestureRecognizer.state {
        case .began:
            initialIndicatorViewFrame = indicatorView.frame
        case .changed:
            var frame = initialIndicatorViewFrame!
            frame.origin.x += gestureRecognizer.translation(in: self).x
            frame.origin.x = max(min(frame.origin.x, bounds.width - indicatorViewInset - frame.width), indicatorViewInset)
            indicatorView.frame = frame
        case .ended, .failed, .cancelled:
            try! setIndex(nearestIndex(toPoint: indicatorView.center))
        default: break
        }
    }
}

// MARK: - UIGestureRecognizerDelegate
extension SwiftySegmentedControl: UIGestureRecognizerDelegate {
    override open func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if gestureRecognizer == panGestureRecognizer {
            return indicatorView.frame.contains(gestureRecognizer.location(in: contentScrollView))
        }
        return super.gestureRecognizerShouldBegin(gestureRecognizer)
    }
}

使用方式

fileprivate func setupControl() {
        let viewSegmentedControl = SwiftySegmentedControl(
            frame: CGRect(x: 0.0, y: 430.0, width: view.bounds.width, height: 50.0),
            titles: ["All", "New", "Pictures", "One", "Two", "Three", "Four", "Five", "Six", "Artists", "Albums", "Recent"],
            index: 1,
            backgroundColor: UIColor(red:0.11, green:0.12, blue:0.13, alpha:1.00),
            titleColor: .white,
            indicatorViewBackgroundColor: UIColor(red:0.11, green:0.12, blue:0.13, alpha:1.00),
            selectedTitleColor: UIColor(red:0.97, green:0.00, blue:0.24, alpha:1.00))
        viewSegmentedControl.autoresizingMask = [.flexibleWidth]
        viewSegmentedControl.indicatorViewInset = 0
        viewSegmentedControl.cornerRadius = 0.0
        viewSegmentedControl.titleFont = UIFont(name: "HelveticaNeue", size: 16.0)!
        viewSegmentedControl.selectedTitleFont = UIFont(name: "HelveticaNeue", size: 16.0)!
        viewSegmentedControl.bouncesOnChange = false
        // 是否禁止拖动选择,注意,这里有个问题我还没改,如果titles长度超过1屏幕,或者说,你想使用下划线,建议禁止拖动选择。
        viewSegmentedControl.panningDisabled = true 
        // 下划线颜色。默认透明
        viewSegmentedControl.indicatorViewLineColor = UIColor.red
        view.addSubview(viewSegmentedControl)
    }

Github: SwiftySegmentedControl

分类: 未分类 标签: