MyException - 我的异常网
当前位置:我的异常网» Delphi » 【Spine】Spine Runtime for Delphi移栽笔记(七)

【Spine】Spine Runtime for Delphi移栽笔记(七)

www.MyException.Cn  网友分享于:2013-08-22  浏览:0次
【Spine】Spine Runtime for Delphi移植笔记(七)
////////////////////////////////////////////////////////////////////////////////
//Generic delphi runtime v3.6 for Spine animation tool                        //
//Runtime port by cjk (hzi1980@163.com)                                       //
////////////////////////////////////////////////////////////////////////////////

unit spine.core.skeleton;

interface

uses
  System.Classes, System.SysUtils, System.Generics.Collections, System.Math,
  spine.types, spine.classes, spine.data,
  spine.core.bone, spine.core.slot, spine.core.skin, spine.core.attachment, spine.core.constraint;

type
  TSpineSkeleton = class(ISkeleton)
  private
    FPathConstraints: TObjectList<TPathConstraint>;
    FIkConstraints: TObjectList<TIkConstraint>;
    FTransformConstraints: TObjectList<TTransformConstraint>;
    FBones: TObjectList<TSpineBone>;
    FSlots: TObjectList<TSpineSlot>;
    FDrawOrder: TList<TSpineSlot>;
    FUpdateCacheList: TList<IUpdateable>;
    FData: TSkeletonData;
    FUpdateCacheReset: TList<TSpineBone>;
    function GetRootBone: TSpineBone;
  private
    procedure SortIkConstraint(const AConstraint: TIkConstraint);
    procedure SortPathConstraint(const AConstraint: TPathConstraint);
    procedure SortTransformConstraint(const AConstraint: TTransformConstraint);
    procedure SortPathConstraintAttachment(const ASkin: TSpineSkin;
      const ASlotIndex: Integer; const ASlotBone: TSpineBone); overload;
    procedure SortPathConstraintAttachment(const AAttachment: IAttachment;
      const ASlotBone: TSpineBone); overload;
    procedure SortBone(const ABone: TSpineBone);
    procedure SortReset(const ABones: TObjectList<TSpineBone>);
  public
    Skin: TSpineSkin;
    R, G, B, A: Single;
    Time: Single;
    X, Y: Single;
    FlipX, FlipY: Boolean;
    property Data: TSkeletonData read FData;
    property Bones: TObjectList<TSpineBone> read FBones;
    property UpdateCacheList: TList<IUpdateable> read FUpdateCacheList;
    property Slots: TObjectList<TSpineSlot> read FSlots;
    property DrawOrder: TList<TSpineSlot> read FDrawOrder;
    property IkConstraints: TObjectList<TIkConstraint> read FIkConstraints;
    property PathConstraints: TObjectList<TPathConstraint> read FPathConstraints;
    property TransformConstraints: TObjectList<TTransformConstraint> read FTransformConstraints;
    property RootBone: TSpineBone read GetRootBone;
  public
    constructor Create(const AData: TSkeletonData);
    destructor Destroy; override;

    procedure UpdateCache;
    procedure UpdateWorldTransform;
    procedure SetToSetupPose;
    procedure SetBonesToSetupPose;
    procedure SetSlotsToSetupPose;
    procedure SetSkin(const ASkinName: string); overload;
    procedure SetSkin(const ASkin: TSpineSkin); overload;
    procedure SetAttachment(const ASlotName, AAttachmentName: string);
    procedure Update(const ADelta: Single);
    procedure GetBounds(out oX, oY, oWidth, oHeight: Single; var AVertexBuffer: TArray<Single>);
    function GetAttachment(const ASlotName, AAttachmentName: string): IAttachment; overload;
    function GetAttachment(const ASlotIndex: Integer; const AAttachmentName: string): IAttachment; overload;
    function FindBone(const ABoneName: string): TSpineBone;
    function FindBoneIndex(const ABoneName: string): Integer;
    function FindSlot(const ASlotName: string): TSpineSlot;
    function FindSlotIndex(const ASlotName: string): Integer;
    function FindIkConstraint(const AConstraintName: string): TIkConstraint;
    function FindTransformConstraint(const AConstraintName: string): TTransformConstraint;
    function FindPathConstraint(const AConstraintName: string): TPathConstraint;
  end;

implementation

{ TSpineSkeleton }

function TSpineSkeleton.GetRootBone: TSpineBone;
begin
  result:= nil;
  if FBones.Count > 0 then
    result:= FBones.Items[0];
end;

constructor TSpineSkeleton.Create(const AData: TSkeletonData);
var
  lBoneData: TBoneData;
  lParentBone, lBone: TSpineBone;
  lSlotData: TSlotData;
  lSlot: TSpineSlot;
  lIkData  : TIkConstraintData;
  lTfData  : TTransformConstraintData;
  lPathData: TPathConstraintData;
begin
  inherited Create;
  R:= 1; G:= 1; B:= 1; A:= 1;
  FUpdateCacheList:= TList<IUpdateable>.Create;
  FUpdateCacheReset:= TList<TSpineBone>.Create;
  FDrawOrder:= TList<TSpineSlot>.Create;
  FBones:= TObjectList<TSpineBone>.Create;
  FSlots:= TObjectList<TSpineSlot>.Create;
  FIkConstraints:= TObjectList<TIkConstraint>.Create;
  FPathConstraints:= TObjectList<TPathConstraint>.Create;
  FTransformConstraints:= TObjectList<TTransformConstraint>.Create;
  if not Assigned(AData) then raise Exception.Create('data cannot be null.');
  FData:= AData;
  for lBoneData in FData.BoneDatas do
  begin
    if not Assigned(lBoneData.Parent) then
      FBones.Add(TSpineBone.Create(lBoneData, Self, nil))
    else
    begin
      lParentBone:= FBones.Items[lBoneData.Parent.Index];
      lBone:= TSpineBone.Create(lBoneData, Self, lParentBone);
      FBones.Add(lBone);
    end;
  end;
  for lSlotData in FData.SlotDatas do
  begin
    lBone:= FBones.Items[lSlotData.BoneData.Index];
    lSlot:= TSpineSlot.Create(lSlotData, lBone);
    FSlots.Add(lSlot);
    FDrawOrder.Add(lSlot);
  end;
  for lIkData in FData.IkConstraintDatas do
    FIkConstraints.Add(TIkConstraint.Create(lIkData, Self));
  for lTfData in FData.TransformConstraintDatas do
    FTransformConstraints.Add(TTransformConstraint.Create(lTfData, Self));
  for lPathData in FData.PathConstraintDatas do
    FPathConstraints.Add(TPathConstraint.Create(lPathData, Self));
  //
  UpdateCache();
  UpdateWorldTransform();
end;

destructor TSpineSkeleton.Destroy;
begin
  FUpdateCacheReset.Free;
  FBones.Free;
  FUpdateCacheList.Free;
  FSlots.Free;
  FDrawOrder.Free;
  FIkConstraints.Free;
  FPathConstraints.Free;
  FTransformConstraints.Free;
  inherited;
end;

procedure TSpineSkeleton.UpdateCache;
var
  i, lConstraintCount, j: Integer;
  lFound: Boolean;
begin
  FUpdateCacheList.Clear;
  FUpdateCacheReset.Clear;
  for i:= 0 to FBones.Count -1 do
    FBones.Items[i].Sorted:= False;
  lConstraintCount:= FIkConstraints.Count +
                     FTransformConstraints.Count +
                     FPathConstraints.Count;
  lFound:= False;
  for i:= 0 to lConstraintCount -1 do
  begin
    for j:= 0 to FIkConstraints.Count -1 do
    begin
      if FIkConstraints[j].Data.Order = j then
      begin
        SortIkConstraint(FIkConstraints[j]);
        lFound:= True;
        break;
      end;
    end;
    if not lFound then
    begin
      for j:= 0 to FTransformConstraints.Count -1 do
      begin
        if FTransformConstraints[j].Data.Order = j then
        begin
          SortTransformConstraint(FTransformConstraints[j]);
          lFound:= True;
          break;
        end;
      end;
    end;
    if not lFound then
    begin
      for j:= 0 to FPathConstraints.Count -1 do
      begin
        if FPathConstraints[j].Data.Order = j then
        begin
          SortPathConstraint(FPathConstraints[j]);
          lFound:= True;
          break;
        end;
      end;
    end;
  end;
  //
  for i:= 0 to FBones.Count -1 do
    SortBone(FBones.Items[i]);
end;

procedure TSpineSkeleton.SortIkConstraint(const AConstraint: TIkConstraint);
var
  lChild: TSpineBone;
begin
  SortBone(AConstraint.Target);
  SortBone(AConstraint.Bones.Items[0]);
  if AConstraint.Bones.Count > 1 then
  begin
    lChild:= AConstraint.Bones.Last;
    if not FUpdateCacheList.Contains(lChild) then
      FUpdateCacheReset.Add(lChild);
  end;
  FUpdateCacheList.Add(AConstraint);
    SortReset(AConstraint.Bones.Items[0].Children);
  AConstraint.Bones.Last.Sorted:= True;
end;

procedure TSpineSkeleton.SortPathConstraint(const AConstraint: TPathConstraint);
var
  lSlot: TSpineSlot;
  lSlotIndex, i: Integer;
  lSlotBone: TSpineBone;
begin
  lSlot:= AConstraint.Target;
  lSlotIndex:= lSlot.Data.Index;
  lSlotBone := lSlot.Bone;
  if Assigned(Skin) then
    SortPathConstraintAttachment(Skin, lSlotIndex, lSlotBone);
  if Assigned(FData.DefaultSkin) and not FData.DefaultSkin.Equals(Skin) then
    SortPathConstraintAttachment(FData.DefaultSkin, lSlotIndex, lSlotBone);
  for i:= 0 to FData.Skins.Count -1 do
    SortPathConstraintAttachment(FData.Skins.Items[i], lSlotIndex, lSlotBone);
  if (lSlot.Attachment is TPathAttachment) then
    SortPathConstraintAttachment(lSlot.Attachment, lSlotBone);
  for i:= 0 to AConstraint.Bones.Count -1 do
    SortBone(AConstraint.Bones.Items[i]);
  //
  FUpdateCacheList.Add(AConstraint);
  for i:= 0 to AConstraint.Bones.Count -1 do
    SortReset(AConstraint.Bones.Items[i].Children);
  for i:= 0 to AConstraint.Bones.Count -1 do
    AConstraint.Bones.Items[i].Sorted:= True;
end;

procedure TSpineSkeleton.SortTransformConstraint(const AConstraint: TTransformConstraint);
var
  i: Integer;
  lChild: TSpineBone;
begin
  SortBone(AConstraint.Target);
  if AConstraint.Data.Local then
  begin
    for i:= 0 to AConstraint.Bones.Count -1 do
    begin
      lChild:= AConstraint.Bones.Items[i];
      SortBone(lChild.Parent);
      if not FUpdateCacheList.Contains(lChild) then
        FUpdateCacheReset.Add(lChild);
    end;
  end else
  begin
    for i:= 0 to AConstraint.Bones.Count -1 do
      SortBone(AConstraint.Bones.Items[i]);
  end;
  FUpdateCacheList.Add(AConstraint);
  for i:= 0 to AConstraint.Bones.Count -1 do
    SortReset(AConstraint.Bones.Items[i].Children);
  for i:= 0 to AConstraint.Bones.Count -1 do
    AConstraint.Bones.Items[i].Sorted:= True;
end;

procedure TSpineSkeleton.SortPathConstraintAttachment(const ASkin: TSpineSkin;
  const ASlotIndex: Integer; const ASlotBone: TSpineBone);
var
  lPair: TPair<TAttachmentKeyTuple,IAttachment>;
begin
  for lPair in Skin.Attachments do
  begin
    if lPair.Key.SlotIndex = ASlotIndex then
      SortPathConstraintAttachment(lPair.Value, ASlotBone);
  end;
end;

procedure TSpineSkeleton.SortPathConstraintAttachment(
  const AAttachment: IAttachment; const ASlotBone: TSpineBone);
var
  lPathBones: TArray<Integer>;
  i, n, nn, boneCount: Integer;
begin
  if not (AAttachment is TPathAttachment) then exit;
  //
  lPathBones:= TPathAttachment(AAttachment).Bones;
  if Length(lPathBones) = 0 then
    SortBone(ASlotBone)
  else
  begin
    i:= 0;
    n:= Length(lPathBones);
    while i < n do
    begin
            boneCount:= lPathBones[i + 1];
            i:= i + 1;
            nn:= i + boneCount;
            while i < nn do
      begin
                SortBone(FBones.Items[lPathBones[i + 1]]);
                i:= i + 1;
            end
    end;
  end;
end;

procedure TSpineSkeleton.SortBone(const ABone: TSpineBone);
begin
  if ABone.Sorted then exit;
  //
  if Assigned(ABone.Parent) then
    SortBone(ABone.Parent);
  ABone.Sorted:= True;
  FUpdateCacheList.Add(ABone);
end;

procedure TSpineSkeleton.SortReset(const ABones: TObjectList<TSpineBone>);
var
  i: Integer;
  lBone: TSpineBone;
begin
  for i:= 0 to ABones.Count -1 do
  begin
    lBone:= ABones.Items[i];
    if lBone.Sorted then
      SortReset(lBone.Children);
    lBone.Sorted:= False;
  end;
end;

procedure TSpineSkeleton.UpdateWorldTransform;
var
  i: Integer;
  lBone: TSpineBone;
begin
  for i:= 0 to FUpdateCacheReset.Count -1 do
  begin
    lBone:= FUpdateCacheReset.Items[i];
    lBone.AppliedX:= lBone.X;
    lBone.AppliedY:= lBone.Y;
    lBone.AppliedRotation:= lBone.Rotation;
    lBone.AppliedScaleX:= lBone.ScaleX;
    lBone.AppliedScaleY:= lBone.ScaleY;
    lBone.AppliedShearX:= lBone.ShearX;
    lBone.AppliedShearY:= lBone.ShearY;
    lBone.AppliedValid:= True;
  end;
  for i:= 0 to FUpdateCacheList.Count -1 do
    FUpdateCacheList.Items[i].Update;
end;

procedure TSpineSkeleton.SetToSetupPose;
begin
  SetBonesToSetupPose();
  SetSlotsToSetupPose();
end;

procedure TSpineSkeleton.SetBonesToSetupPose;
var
  i: Integer;
begin
  for i:= 0 to FBones.Count -1 do
    FBones.Items[i].SetToSetupPose;
  for i:= 0 to FIkConstraints.Count -1 do
  begin
    with FIkConstraints.Items[i] do
    begin
      BendDirection:= Data.BendDirection;
      Mix:= Data.Mix;
    end;
  end;
  for i:= 0 to FTransformConstraints.Count -1 do
  begin
    with FTransformConstraints.Items[i] do
    begin
      RotateMix:= Data.RotateMix;
      TranslateMix:= Data.TranslateMix;
      ScaleMix:= Data.ScaleMix;
      ShearMix:= Data.ShearMix;
    end;
  end;
  for i:= 0 to FPathConstraints.Count -1 do
  begin
    with FPathConstraints.Items[i] do
    begin
      Position:= Data.Position;
      Spacing:= Data.Spacing;
      RotateMix:= Data.RotateMix;
      TranslateMix:= Data.TranslateMix;
    end;
  end;
end;

procedure TSpineSkeleton.SetSlotsToSetupPose;
var
  i: Integer;
begin
  FDrawOrder.Clear;
  for i:= 0 to FSlots.Count -1 do
    FDrawOrder.Add(FSlots.Items[i]);
  for i:= 0 to FSlots.Count -1 do
    FSlots.Items[i].SetToSetupPose;
end;

function TSpineSkeleton.FindBone(const ABoneName: string): TSpineBone;
var
  i: Integer;
begin
  if ABoneName.Trim.IsEmpty then raise Exception.Create('boneName cannot be null.');
  for i:= 0 to FBones.Count -1 do
  begin
    if FBones.Items[i].Data.Name = ABoneName then
      exit(FBones.Items[i]);
  end;
  result:= nil;
end;

function TSpineSkeleton.FindBoneIndex(const ABoneName: string): Integer;
var
  i: Integer;
begin
  result:= -1;
  if ABoneName.Trim.IsEmpty then raise Exception.Create('boneName cannot be null.');
  for i:= 0 to FBones.Count -1 do
  begin
    if FBones.Items[i].Data.Name = ABoneName then
      exit(i);
  end;
end;

function TSpineSkeleton.FindSlot(const ASlotName: string): TSpineSlot;
var
  i: Integer;
begin
  result:= nil;
  if ASlotName.Trim.IsEmpty then raise Exception.Create('slotName cannot be null.');
  for i:= 0 to FSlots.Count -1 do
  begin
    if FSlots.Items[i].Data.Name = ASlotName then
      exit(FSlots.Items[i]);
  end;
end;

function TSpineSkeleton.FindSlotIndex(const ASlotName: string): Integer;
var
  i: Integer;
begin
  result:= -1;
  if ASlotName.Trim.IsEmpty then raise Exception.Create('slotName cannot be null.');
  for i:= 0 to FSlots.Count -1 do
  begin
    if FSlots.Items[i].Data.Name = ASlotName then
      exit(i);
  end;
end;

procedure TSpineSkeleton.SetSkin(const ASkinName: string);
var
  lSkin: TSpineSkin;
begin
  lSkin:= FData.FindSkin(ASkinName);
  if not Assigned(lSkin) then raise Exception.CreateFmt('Skin not found: %s',[ASkinName]);
    SetSkin(lSkin);
end;

procedure TSpineSkeleton.SetSkin(const ASkin: TSpineSkin);
var
  i: Integer;
  lAttachmentName: string;
  lAttachment: IAttachment;
begin
  if Assigned(Self.Skin) then
    ASkin.AttachAll(Self, Self.Skin)
  else
  begin
    for i:= 0 to FSlots.Count -1 do
    begin
      lAttachmentName:= FSlots.Items[i].Data.AttachmentName;
      if not lAttachmentName.Trim.IsEmpty then
      begin
        lAttachment:= ASkin.GetAttachment(i, lAttachmentName);
        if Assigned(lAttachment) then
          FSlots.Items[i].Attachment:= lAttachment;
      end;
    end;
  end;
  Self.Skin:= ASkin;
end;

function TSpineSkeleton.GetAttachment(const ASlotName,
  AAttachmentName: string): IAttachment;
begin
  result:= GetAttachment(FData.FindSlotIndex(ASlotName), AAttachmentName);
end;

function TSpineSkeleton.GetAttachment(const ASlotIndex: Integer;
  const AAttachmentName: string): IAttachment;
var
  lAttachment: IAttachment;
begin
  result:= nil;
  if AAttachmentName.Trim.IsEmpty then raise Exception.Create('attachmentName cannot be null.');
  if Assigned(Self.Skin) then
  begin
    lAttachment:= Self.Skin.GetAttachment(ASlotIndex, AAttachmentName);
    if Assigned(lAttachment) then exit(lAttachment);
  end;
  if Assigned(FData.DefaultSkin) then
    result:= FData.DefaultSkin.GetAttachment(ASlotIndex, AAttachmentName);
end;

procedure TSpineSkeleton.SetAttachment(const ASlotName,
  AAttachmentName: string);
var
  i: Integer;
  lSlot: TSpineSlot;
  lAttachment: IAttachment;
begin
  if ASlotName.Trim.IsEmpty then raise Exception.Create('slotName cannot be null.');
  for i:= 0 to FSlots.Count -1 do
  begin
    lSlot:= FSlots.Items[i];
    if lSlot.Data.Name.Equals(ASlotName) then
    begin
      lAttachment:= nil;
      if not AAttachmentName.Trim.IsEmpty then
      begin
        lAttachment:= Self.GetAttachment(i, AAttachmentName);
        if not Assigned(lAttachment) then
          raise Exception.CreateFmt('Attachment not found: %s, for slot: %s',[AAttachmentName,ASlotName]);
      end;
      lSlot.Attachment:= lAttachment;
      exit;
    end;
  end;
  raise Exception.CreateFmt('Slot not found: ',[ASlotName]);
end;

function TSpineSkeleton.FindIkConstraint(
  const AConstraintName: string): TIkConstraint;
var
  i: Integer;
  lConstraint: TIkConstraint;
begin
  result:= nil;
  if AConstraintName.Trim.IsEmpty then raise Exception.Create('constraintName cannot be null.');
  for i:= 0 to FIkConstraints.Count -1 do
  begin
    lConstraint:= FIkConstraints.Items[i];
    if lConstraint.Data.Name.Equals(AConstraintName) then exit(lConstraint);
  end;
end;

function TSpineSkeleton.FindTransformConstraint(
  const AConstraintName: string): TTransformConstraint;
var
  i: Integer;
  lConstraint: TTransformConstraint;
begin
  result:= nil;
  if AConstraintName.Trim.IsEmpty then raise Exception.Create('constraintName cannot be null.');
  for i:= 0 to FTransformConstraints.Count -1 do
  begin
    lConstraint:= FTransformConstraints.Items[i];
    if lConstraint.Data.Name.Equals(AConstraintName) then exit(lConstraint);
  end;
end;

function TSpineSkeleton.FindPathConstraint(
  const AConstraintName: string): TPathConstraint;
var
  i: Integer;
  lConstraint: TPathConstraint;
begin
  result:= nil;
  if AConstraintName.Trim.IsEmpty then raise Exception.Create('constraintName cannot be null.');
  for i:= 0 to FPathConstraints.Count -1 do
  begin
    lConstraint:= FPathConstraints.Items[i];
    if lConstraint.Data.Name.Equals(AConstraintName) then exit(lConstraint);
  end;
end;

procedure TSpineSkeleton.Update(const ADelta: Single);
begin
  Self.Time:= Self.Time + ADelta;
end;

procedure TSpineSkeleton.GetBounds(out oX, oY, oWidth, oHeight: Single;
  var AVertexBuffer: TArray<Single>);
var
  lTemp, lVertices: TArray<Single>;
  i, lVerticesLength, j, n: Integer;
  lMinX, lMinY, lMaxX, lMaxY, lVX, lVY: Single;
  lSlot: TSpineSlot;
  lAttachment: IAttachment;
  lRegionAttachment: TRegionAttachment;
  lMeshAttachment: TMeshAttachment;
begin
  lTemp:= AVertexBuffer;
  if Length(lTemp) = 0 then
    SetLength(lTemp, 8);
  lMinX:= Integer.MaxValue;
  lMinY:= Integer.MaxValue;
  lMaxX:= Integer.MinValue;
  lMaxY:= Integer.MinValue;
  for i:= 0 to FDrawOrder.Count -1 do
  begin
    lSlot:= FDrawOrder.Items[i];
    lVerticesLength:= 0;
    SetLength(lVertices, 0);
    lAttachment:= lSlot.Attachment;
    lRegionAttachment:= lAttachment as TRegionAttachment;
    if Assigned(lRegionAttachment) then
    begin
      lVerticesLength:= 8;
      lVertices:= lTemp;
      if Length(lVertices) < 8 then
      begin
        SetLength(lTemp, 8);
        lVertices:= lTemp;
        lRegionAttachment.ComputeWorldVertices(lSlot.Bone, lTemp, 0);
      end;
    end else
    begin
      lMeshAttachment:= lAttachment as TMeshAttachment;
      if Assigned(lMeshAttachment) then
      begin
        lVerticesLength:= lMeshAttachment.WorldVerticesLength;
        lVertices:= lTemp;
        if Length(lVertices) < lVerticesLength then
        begin
          SetLength(lTemp, lVerticesLength);
          lVertices:= lTemp;
          lMeshAttachment.ComputeWorldVertices(lSlot, 0, lVerticesLength, lTemp, 0);
        end;
      end;
    end;
    n:= Length(lVertices);
    if n > 0 then
    begin
      j:= 0;
      while j < n do
      begin
        lVX:= lVertices[j];
        lVY:= lVertices[j+1];
        lMinX:= Min(lMinX, lVX);
        lMinY:= Min(lMinY, lVY);
        lMaxX:= Max(lMaxX, lVX);
        lMaxY:= Max(lMaxY, lVY);
        j:= j + 2;
      end;
    end;
  end;
  oX:= lMinX;
  oY:= lMinY;
  oWidth := lMaxX - lMinX;
  oHeight:= lMaxY - lMinY;
  AVertexBuffer:= lTemp;
end;

end.

骨架模块,粗粗检查了一下,没什么问题。

这里说一下,这个库的移植(其实大多是翻译,因为很多代码我根本不理解,但是不影响我翻译就是了)是基于C#的,C#是垃圾回收机制,移到delphi的话,对象的创建、释放就要很注意,不然很容易有内存泄露。

这边检查主要就是检查对象的释放,大多是TList和TObjectList的选择问题。

文章评论

程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
一个程序员的时间管理
一个程序员的时间管理
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
我是如何打败拖延症的
我是如何打败拖延症的
总结2014中国互联网十大段子
总结2014中国互联网十大段子
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
代码女神横空出世
代码女神横空出世
程序员的鄙视链
程序员的鄙视链
鲜为人知的编程真相
鲜为人知的编程真相
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员和编码员之间的区别
程序员和编码员之间的区别
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员必看的十大电影
程序员必看的十大电影
如何成为一名黑客
如何成为一名黑客
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
我的丈夫是个程序员
我的丈夫是个程序员
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
程序员都该阅读的书
程序员都该阅读的书
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
Java程序员必看电影
Java程序员必看电影
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
程序员应该关注的一些事儿
程序员应该关注的一些事儿
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
漫画:程序员的工作
漫画:程序员的工作
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
中美印日四国程序员比较
中美印日四国程序员比较
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
10个调试和排错的小建议
10个调试和排错的小建议
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有